Skip to content

Commit

Permalink
breaking: Device Tracker now uses full platform, device_tracker names…
Browse files Browse the repository at this point in the history
…/ids will change. (#84)

- previously we used the "seen" service to indicate when device_tracker entities should be HOME or NOT_HOME. This caused the entities to be stored in the `known_devices.yaml` file and not in the entity registry, which meant they could not be renamed and would not show in the entity listing for our integration.
- Using the platform directly (see `device_tracker.py` we now get entries in the registry and device trackers can be listed in the integration and renamed etc.
  • Loading branch information
agittins authored Jan 8, 2024
1 parent 059af3b commit 8986642
Show file tree
Hide file tree
Showing 3 changed files with 85 additions and 53 deletions.
69 changes: 17 additions & 52 deletions custom_components/bermuda/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,8 @@
PassiveBluetoothDataUpdateCoordinator,
)
from homeassistant.config_entries import ConfigEntry
from homeassistant.const import STATE_HOME
from homeassistant.const import STATE_NOT_HOME
from homeassistant.core import callback
from homeassistant.core import Config
from homeassistant.core import HomeAssistant
Expand Down Expand Up @@ -284,7 +286,8 @@ def update(
self.tx_power is not None
and scandata.advertisement.tx_power != self.tx_power
):
# Not really an erorr, we just don't account for this happening. I want to know if it does.
# Not really an erorr, we just don't account for this happening -
# I want to know if it does.
_LOGGER.warning(
"Device changed TX-POWER! That was unexpected: %s %sdB",
device_address,
Expand Down Expand Up @@ -340,12 +343,11 @@ def __init__(self, address, options):
self.area_distance: float = None # how far this dev is from that area
self.area_rssi: float = None # rssi from closest scanner
self.area_scanner: str = None # name of closest scanner
self.zone: str = None # home or not_home
self.zone: str = None # STATE_HOME or STATE_NOT_HOME
self.manufacturer: str = None
self.connectable: bool = False
self.is_scanner: bool = False
self.entry_id: str = None # used for scanner devices
self.send_tracker_see: bool = False # Create/update device_tracker entity
self.create_sensor: bool = False # Create/update a sensor for this device
self.last_seen: float = (
0 # stamp from most recent scanner spotting. MONOTONIC_TIME
Expand Down Expand Up @@ -548,8 +550,7 @@ async def _async_update_data(self):
device.connectable = service_info.connectable

# Try to make a nice name for prefname.
# TODO: Add support for user-defined name, especially since the
# device_tracker entry can only be renamed using the editor.
# TODO: Add support for user-defined name.
if device.prefname is None or device.prefname.startswith(DOMAIN + "_"):
device.prefname = (
device.name
Expand Down Expand Up @@ -584,60 +585,24 @@ async def _async_update_data(self):
device.add_scanner(scanner_device, discovered)

if device.address.upper() in self.options.get(CONF_DEVICES, []):
device.send_tracker_see = True
# This is a device we track. Set it up:

device.create_sensor = True

if device.send_tracker_see:
# Send a "see" notification to device_tracker
await self._send_device_tracker_see(device)
# Update whether the device has been seen recently, for device_tracker:
if (
MONOTONIC_TIME()
- self.options.get(CONF_DEVTRACK_TIMEOUT, DEFAULT_DEVTRACK_TIMEOUT)
< device.last_seen
):
device.zone = STATE_HOME
else:
device.zone = STATE_NOT_HOME

self._refresh_areas_by_min_distance()

# end of async update

async def _send_device_tracker_see(self, device: BermudaDevice):
"""Send "see" event to the legacy device_tracker integration.
If the device is not yet in known_devices.yaml it will get added.
Note that device_tracker can *only* support [home|not_home],
because device_tracker only deals with "Zones" not "Areas".
Simply calling the "see" service is the simplest way to
get this done, but if we need more control (eg, specifying
the source (gps|router|etc)) we might need to hook and implement
it specifically. This is probably all we need right now though:
TODO: Allow user to configure what name to use for the device_tracker.
"""

# Check if the device has been seen recently
if (
MONOTONIC_TIME()
- self.options.get(CONF_DEVTRACK_TIMEOUT, DEFAULT_DEVTRACK_TIMEOUT)
< device.last_seen
):
device.zone = "home"
else:
device.zone = "not_home"

# If mac is set, device_tracker will override our dev_id
# with slugified (hostname OR mac). We don't want that
# since we want dev_id (the key in known_devices.yaml) to
# be stable, predictable and identifyably ours.
#
# So, we will not set mac, but use bermuda_[mac] as dev_id
# and prefname or user-supplied name for host_name.
await self.hass.services.async_call(
domain="device_tracker",
service="see",
service_data={
"dev_id": "bermuda_" + slugify(device.address),
# 'mac': device.address,
"host_name": device.prefname,
"location_name": device.zone,
},
)

def dt_mono_to_datetime(self, stamp) -> datetime:
"""Given a monotonic timestamp, convert to datetime object"""
age = MONOTONIC_TIME() - stamp
Expand Down
3 changes: 2 additions & 1 deletion custom_components/bermuda/const.py
Original file line number Diff line number Diff line change
Expand Up @@ -23,8 +23,9 @@
BINARY_SENSOR = "binary_sensor"
SENSOR = "sensor"
SWITCH = "switch"
DEVICE_TRACKER = "device_tracker"
# PLATFORMS = [BINARY_SENSOR, SENSOR, SWITCH]
PLATFORMS = [SENSOR]
PLATFORMS = [SENSOR, DEVICE_TRACKER]

DOCS = {}

Expand Down
66 changes: 66 additions & 0 deletions custom_components/bermuda/device_tracker.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,66 @@
"""Create device_tracker entities for Bermuda devices"""
from __future__ import annotations

import logging
from collections.abc import Mapping

from homeassistant.components.device_tracker import SourceType
from homeassistant.components.device_tracker.config_entry import BaseTrackerEntity
from homeassistant.config_entries import ConfigEntry
from homeassistant.const import STATE_HOME
from homeassistant.core import HomeAssistant
from homeassistant.helpers.entity_platform import AddEntitiesCallback

from . import BermudaDataUpdateCoordinator
from .const import DOMAIN
from .entity import BermudaEntity

_LOGGER = logging.getLogger(__name__)


async def async_setup_entry(
hass: HomeAssistant,
entry: ConfigEntry,
async_add_devices: AddEntitiesCallback,
) -> None:
"""Load Device Tracker entities for a config entry."""
coordinator: BermudaDataUpdateCoordinator = hass.data[DOMAIN][entry.entry_id]

# We go through each "device" in the co-ordinator, and create the entities
entities = []
for device in coordinator.devices.values():
if device.create_sensor:
entities.append(BermudaDeviceTracker(coordinator, entry, device.address))
async_add_devices(entities, True)


class BermudaDeviceTracker(BermudaEntity, BaseTrackerEntity):
"""A trackable Bermuda Device."""

_attr_should_poll = False
_attr_has_entity_name = True
_attr_name = None

@property
def extra_state_attributes(self) -> Mapping[str, str]:
"""Return extra state attributes for this device."""
return {"scanner": self._device.area_scanner, "area": self._device.area_name}

@property
def state(self) -> str:
"""Return the state of the device."""
return self._device.zone

@property
def source_type(self) -> SourceType:
"""Return the source type, eg gps or router, of the device."""
return SourceType.BLUETOOTH_LE

@property
def icon(self) -> str:
"""Return device icon."""
return (
"mdi:bluetooth-connect"
if self._device.zone == STATE_HOME
else "mdi:bluetooth-off"
)

0 comments on commit 8986642

Please sign in to comment.