Skip to content

Commit

Permalink
Support dynamic icons for binary sensors (#336)
Browse files Browse the repository at this point in the history
* Move icons to own module

* Remove icons from const

* Update sensors to use new icons module

* Add dynamic binary sensor icons

* Support switch icons

* Format files and fix tests

* Cleanup return

Co-authored-by: Dermot Duffy <dermot.duffy@gmail.com>

* Cleanup return

Co-authored-by: Dermot Duffy <dermot.duffy@gmail.com>

* Cleanup return

Co-authored-by: Dermot Duffy <dermot.duffy@gmail.com>

* Remove odd boolean type

* Fix indentation

Co-authored-by: Dermot Duffy <dermot.duffy@gmail.com>
  • Loading branch information
NickM-27 and dermotduffy committed Sep 16, 2022
1 parent 1c96743 commit 387af4d
Show file tree
Hide file tree
Showing 9 changed files with 136 additions and 83 deletions.
6 changes: 6 additions & 0 deletions custom_components/frigate/binary_sensor.py
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@
get_zones,
)
from .const import ATTR_CONFIG, DOMAIN, NAME
from .icons import get_dynamic_icon_from_type

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

Expand Down Expand Up @@ -135,6 +136,11 @@ def device_class(self) -> str:
"""Return the device class."""
return cast(str, DEVICE_CLASS_OCCUPANCY)

@property
def icon(self) -> str:
"""Return the icon of the sensor."""
return get_dynamic_icon_from_type(self._obj_name, self._is_on)


class FrigateMotionSensor(FrigateMQTTEntity, BinarySensorEntity): # type: ignore[misc]
"""Frigate Motion Sensor class."""
Expand Down
18 changes: 0 additions & 18 deletions custom_components/frigate/const.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,24 +6,6 @@
FRIGATE_RELEASES_URL = "https://github.com/blakeblackshear/frigate/releases"
FRIGATE_RELEASE_TAG_URL = f"{FRIGATE_RELEASES_URL}/tag"

# Icons
ICON_BICYCLE = "mdi:bicycle"
ICON_CAR = "mdi:car"
ICON_CAT = "mdi:cat"
ICON_CONTRAST = "mdi:contrast-circle"
ICON_CORAL = "mdi:scoreboard-outline"
ICON_COW = "mdi:cow"
ICON_DOG = "mdi:dog-side"
ICON_FILM_MULTIPLE = "mdi:filmstrip-box-multiple"
ICON_HORSE = "mdi:horse"
ICON_IMAGE_MULTIPLE = "mdi:image-multiple"
ICON_MOTION_SENSOR = "hass:motion-sensor"
ICON_MOTORCYCLE = "mdi:motorbike"
ICON_OTHER = "mdi:shield-alert"
ICON_PERSON = "mdi:human"
ICON_SERVER = "mdi:server"
ICON_SPEEDOMETER = "mdi:speedometer"

# Platforms
BINARY_SENSOR = "binary_sensor"
NUMBER = "number"
Expand Down
70 changes: 70 additions & 0 deletions custom_components/frigate/icons.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,70 @@
"""Handles icons for different entity types."""

ICON_BICYCLE = "mdi:bicycle"
ICON_CAR = "mdi:car"
ICON_CAT = "mdi:cat"
ICON_CONTRAST = "mdi:contrast-circle"
ICON_CORAL = "mdi:scoreboard-outline"
ICON_COW = "mdi:cow"
ICON_DOG = "mdi:dog-side"
ICON_FILM_MULTIPLE = "mdi:filmstrip-box-multiple"
ICON_HORSE = "mdi:horse"
ICON_IMAGE_MULTIPLE = "mdi:image-multiple"
ICON_MOTION_SENSOR = "mdi:motion-sensor"
ICON_MOTORCYCLE = "mdi:motorbike"
ICON_OTHER = "mdi:shield-alert"
ICON_PERSON = "mdi:human"
ICON_SERVER = "mdi:server"
ICON_SPEEDOMETER = "mdi:speedometer"

ICON_DEFAULT_ON = "mdi:home"

ICON_CAR_OFF = "mdi:car-off"
ICON_DEFAULT_OFF = "mdi:home-outline"
ICON_DOG_OFF = "mdi:dog-side-off"


def get_dynamic_icon_from_type(obj_type: str, is_on: bool) -> str:
"""Get icon for a specific object type and current state."""

if obj_type == "car":
return ICON_CAR if is_on else ICON_CAR_OFF
if obj_type == "dog":
return ICON_DOG if is_on else ICON_DOG_OFF

return ICON_DEFAULT_ON if is_on else ICON_DEFAULT_OFF


def get_icon_from_switch(switch_type: str) -> str:
"""Get icon for a specific switch type."""
if switch_type == "snapshots":
return ICON_IMAGE_MULTIPLE
if switch_type == "recordings":
return ICON_FILM_MULTIPLE
if switch_type == "improve_contrast":
return ICON_CONTRAST

return ICON_MOTION_SENSOR


def get_icon_from_type(obj_type: str) -> str:
"""Get icon for a specific object type."""

if obj_type == "person":
return ICON_PERSON
if obj_type == "car":
return ICON_CAR
if obj_type == "dog":
return ICON_DOG
if obj_type == "cat":
return ICON_CAT
if obj_type == "motorcycle":
return ICON_MOTORCYCLE
if obj_type == "bicycle":
return ICON_BICYCLE
if obj_type == "cow":
return ICON_COW
if obj_type == "horse":
return ICON_HORSE

return ICON_OTHER
2 changes: 1 addition & 1 deletion custom_components/frigate/number.py
Original file line number Diff line number Diff line change
Expand Up @@ -23,13 +23,13 @@
from .const import (
ATTR_CONFIG,
DOMAIN,
ICON_SPEEDOMETER,
MAX_CONTOUR_AREA,
MAX_THRESHOLD,
MIN_CONTOUR_AREA,
MIN_THRESHOLD,
NAME,
)
from .icons import ICON_SPEEDOMETER

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

Expand Down
42 changes: 3 additions & 39 deletions custom_components/frigate/sensor.py
Original file line number Diff line number Diff line change
Expand Up @@ -22,26 +22,8 @@
get_frigate_entity_unique_id,
get_zones,
)
from .const import (
ATTR_CONFIG,
ATTR_COORDINATOR,
DOMAIN,
FPS,
ICON_BICYCLE,
ICON_CAR,
ICON_CAT,
ICON_CORAL,
ICON_COW,
ICON_DOG,
ICON_HORSE,
ICON_MOTORCYCLE,
ICON_OTHER,
ICON_PERSON,
ICON_SERVER,
ICON_SPEEDOMETER,
MS,
NAME,
)
from .const import ATTR_CONFIG, ATTR_COORDINATOR, DOMAIN, FPS, MS, NAME
from .icons import ICON_CORAL, ICON_SERVER, ICON_SPEEDOMETER, get_icon_from_type

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

Expand Down Expand Up @@ -334,25 +316,7 @@ def __init__(
self._obj_name = obj_name
self._state = 0
self._frigate_config = frigate_config

if self._obj_name == "person":
self._icon = ICON_PERSON
elif self._obj_name == "car":
self._icon = ICON_CAR
elif self._obj_name == "dog":
self._icon = ICON_DOG
elif self._obj_name == "cat":
self._icon = ICON_CAT
elif self._obj_name == "motorcycle":
self._icon = ICON_MOTORCYCLE
elif self._obj_name == "bicycle":
self._icon = ICON_BICYCLE
elif self._obj_name == "cow":
self._icon = ICON_COW
elif self._obj_name == "horse":
self._icon = ICON_HORSE
else:
self._icon = ICON_OTHER
self._icon = get_icon_from_type(self._obj_name)

super().__init__(
config_entry,
Expand Down
22 changes: 3 additions & 19 deletions custom_components/frigate/switch.py
Original file line number Diff line number Diff line change
Expand Up @@ -19,15 +19,8 @@
get_frigate_device_identifier,
get_frigate_entity_unique_id,
)
from .const import (
ATTR_CONFIG,
DOMAIN,
ICON_CONTRAST,
ICON_FILM_MULTIPLE,
ICON_IMAGE_MULTIPLE,
ICON_MOTION_SENSOR,
NAME,
)
from .const import ATTR_CONFIG, DOMAIN, NAME
from .icons import get_icon_from_switch

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

Expand Down Expand Up @@ -76,16 +69,7 @@ def __init__(
)

self._attr_entity_registry_enabled_default = default_enabled

if self._switch_name == "snapshots":
self._icon = ICON_IMAGE_MULTIPLE
elif self._switch_name == "recordings":
self._icon = ICON_FILM_MULTIPLE
elif self._switch_name == "improve_contrast":
self._icon = ICON_CONTRAST
else:
self._icon = ICON_MOTION_SENSOR

self._icon = get_icon_from_switch(self._switch_name)
super().__init__(
config_entry,
frigate_config,
Expand Down
50 changes: 50 additions & 0 deletions tests/test_icons.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
"""Tests frigate icons."""

from custom_components.frigate.icons import (
get_dynamic_icon_from_type,
get_icon_from_switch,
get_icon_from_type,
)


async def test_get_binary_sensor_icons() -> None:
"""Test sensor icon logic."""
assert get_dynamic_icon_from_type("person", True) == "mdi:home"
assert get_dynamic_icon_from_type("person", False) == "mdi:home-outline"
assert get_dynamic_icon_from_type("car", True) == "mdi:car"
assert get_dynamic_icon_from_type("car", False) == "mdi:car-off"
assert get_dynamic_icon_from_type("dog", True) == "mdi:dog-side"
assert get_dynamic_icon_from_type("dog", False) == "mdi:dog-side-off"
assert get_dynamic_icon_from_type("cat", True) == "mdi:home"
assert get_dynamic_icon_from_type("cat", False) == "mdi:home-outline"
assert get_dynamic_icon_from_type("motorcycle", True) == "mdi:home"
assert get_dynamic_icon_from_type("motorcycle", False) == "mdi:home-outline"
assert get_dynamic_icon_from_type("bicycle", True) == "mdi:home"
assert get_dynamic_icon_from_type("bycicle", False) == "mdi:home-outline"
assert get_dynamic_icon_from_type("cow", True) == "mdi:home"
assert get_dynamic_icon_from_type("cow", False) == "mdi:home-outline"
assert get_dynamic_icon_from_type("horse", True) == "mdi:home"
assert get_dynamic_icon_from_type("horse", False) == "mdi:home-outline"
assert get_dynamic_icon_from_type("other", True) == "mdi:home"
assert get_dynamic_icon_from_type("other", False) == "mdi:home-outline"


async def test_get_sensor_icons() -> None:
"""Test sensor icon logic."""
assert get_icon_from_type("person") == "mdi:human"
assert get_icon_from_type("car") == "mdi:car"
assert get_icon_from_type("dog") == "mdi:dog-side"
assert get_icon_from_type("cat") == "mdi:cat"
assert get_icon_from_type("motorcycle") == "mdi:motorbike"
assert get_icon_from_type("bicycle") == "mdi:bicycle"
assert get_icon_from_type("cow") == "mdi:cow"
assert get_icon_from_type("horse") == "mdi:horse"
assert get_icon_from_type("other") == "mdi:shield-alert"


async def test_get_switch_icons() -> None:
"""Test switch icon logic."""
assert get_icon_from_switch("improve_contrast") == "mdi:contrast-circle"
assert get_icon_from_switch("snapshots") == "mdi:image-multiple"
assert get_icon_from_switch("recordings") == "mdi:filmstrip-box-multiple"
assert get_icon_from_switch("motion") == "mdi:motion-sensor"
7 changes: 2 additions & 5 deletions tests/test_sensor.py
Original file line number Diff line number Diff line change
Expand Up @@ -14,9 +14,8 @@

from custom_components.frigate import SCAN_INTERVAL
from custom_components.frigate.api import FrigateApiClientError
from custom_components.frigate.const import (
DOMAIN,
FPS,
from custom_components.frigate.const import DOMAIN, FPS, MS, NAME
from custom_components.frigate.icons import (
ICON_BICYCLE,
ICON_CAR,
ICON_CAT,
Expand All @@ -29,8 +28,6 @@
ICON_PERSON,
ICON_SERVER,
ICON_SPEEDOMETER,
MS,
NAME,
)
from homeassistant.const import TEMP_CELSIUS
from homeassistant.core import HomeAssistant
Expand Down
2 changes: 1 addition & 1 deletion tests/test_switch.py
Original file line number Diff line number Diff line change
Expand Up @@ -147,7 +147,7 @@ async def test_switch_icon(hass: HomeAssistant) -> None:
await setup_mock_frigate_config_entry(hass)

expected_results = {
TEST_SWITCH_FRONT_DOOR_DETECT_ENTITY_ID: "hass:motion-sensor",
TEST_SWITCH_FRONT_DOOR_DETECT_ENTITY_ID: "mdi:motion-sensor",
TEST_SWITCH_FRONT_DOOR_RECORDINGS_ENTITY_ID: "mdi:filmstrip-box-multiple",
TEST_SWITCH_FRONT_DOOR_SNAPSHOTS_ENTITY_ID: "mdi:image-multiple",
}
Expand Down

0 comments on commit 387af4d

Please sign in to comment.