Skip to content
Closed
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
7 changes: 6 additions & 1 deletion .github/workflows/lint.yml
Original file line number Diff line number Diff line change
Expand Up @@ -8,14 +8,19 @@ on:
jobs:
pre-commit:
runs-on: ubuntu-latest
strategy:
matrix:
python-version:
- "3.13"
- "3.14"

steps:
- uses: actions/checkout@v6

- name: Set up Python
uses: actions/setup-python@v6
with:
python-version: "3.13"
python-version: ${{ matrix.python-version }}

- name: Install uv
uses: astral-sh/setup-uv@v7
Expand Down
62 changes: 60 additions & 2 deletions custom_components/vantage/binary_sensor.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,9 +3,14 @@
from typing import override

from aiovantage.controllers import Controller
from aiovantage.objects import DryContact
from aiovantage.events import Connected, Disconnected, Reconnected
from aiovantage.objects import DryContact, Master

from homeassistant.components.binary_sensor import BinarySensorEntity
from homeassistant.components.binary_sensor import (
BinarySensorDeviceClass,
BinarySensorEntity,
)
from homeassistant.const import EntityCategory
from homeassistant.core import HomeAssistant
from homeassistant.helpers.entity_platform import AddEntitiesCallback

Expand All @@ -26,6 +31,11 @@ async def async_setup_entry(
entry, async_add_entities, VantageBinarySensorEntity, vantage.dry_contacts
)

# Add a connectivity binary sensor for each controller
add_entities_from_controller(
entry, async_add_entities, VantageControllerConnectivityEntity, vantage.masters
)


class VantageBinarySensorEntity(VantageEntity[DryContact], BinarySensorEntity):
"""Binary sensor entity provided by a Vantage DryContact object."""
Expand All @@ -47,3 +57,51 @@ def __init__(
@override
def is_on(self) -> bool | None:
return self.obj.is_down


class VantageControllerConnectivityEntity(VantageEntity[Master], BinarySensorEntity):
"""Binary sensor reflecting the live connection to a Vantage controller.

Unlike the per-object entities, this sensor stays available and reports `off`
while the controller is unreachable, giving a single reliable signal that does
not depend on individual object polls failing one by one.
"""

_attr_device_class = BinarySensorDeviceClass.CONNECTIVITY
_attr_entity_category = EntityCategory.DIAGNOSTIC

# Override the base "vantage" translation key so the name comes from the
# connectivity device class ("Connectivity").
_attr_translation_key = None

# Setup only completes after a successful initialize(), so the event stream is
# connected at this point. Connection events keep this in sync afterwards.
_attr_is_on = True

@property
@override
def unique_id(self) -> str:
return f"{self.obj.vid}:connectivity"

@property
@override
def is_on(self) -> bool | None:
return self._attr_is_on

@override
async def async_added_to_hass(self) -> None:
await super().async_added_to_hass()

event_stream = self.client.event_stream

def set_connected(_event: Connected | Reconnected) -> None:
self._attr_is_on = True
self.async_write_ha_state()

def set_disconnected(_event: Disconnected) -> None:
self._attr_is_on = False
self.async_write_ha_state()

self.async_on_remove(event_stream.subscribe(Connected, set_connected))
self.async_on_remove(event_stream.subscribe(Reconnected, set_connected))
self.async_on_remove(event_stream.subscribe(Disconnected, set_disconnected))
Loading