Skip to content
Merged
Show file tree
Hide file tree
Changes from 2 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
11 changes: 8 additions & 3 deletions homeassistant/components/sun/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,10 @@
# as we will always load it and we do not want to have
# to wait for the import executor when its busy later
# in the startup process.
from . import sensor as sensor_pre_import # noqa: F401
from . import (
binary_sensor as binary_sensor_pre_import, # noqa: F401
sensor as sensor_pre_import, # noqa: F401
)
from .const import ( # noqa: F401 # noqa: F401
DOMAIN,
STATE_ABOVE_HORIZON,
Expand Down Expand Up @@ -52,14 +55,16 @@ async def async_setup_entry(hass: HomeAssistant, entry: SunConfigEntry) -> bool:
await component.async_add_entities([sun])
entry.runtime_data = sun
entry.async_on_unload(sun.remove_listeners)
await hass.config_entries.async_forward_entry_setups(entry, [Platform.SENSOR])
await hass.config_entries.async_forward_entry_setups(
entry, [Platform.SENSOR, Platform.BINARY_SENSOR]
Comment thread
MartinHjelmare marked this conversation as resolved.
Outdated
)
return True


async def async_unload_entry(hass: HomeAssistant, entry: SunConfigEntry) -> bool:
"""Unload a config entry."""
if unload_ok := await hass.config_entries.async_unload_platforms(
entry, [Platform.SENSOR]
entry, [Platform.SENSOR, Platform.BINARY_SENSOR]
Comment thread
MartinHjelmare marked this conversation as resolved.
Outdated
):
await entry.runtime_data.async_remove()
return unload_ok
100 changes: 100 additions & 0 deletions homeassistant/components/sun/binary_sensor.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,100 @@
"""Sensor platform for Sun integration."""
Comment thread
MartinHjelmare marked this conversation as resolved.
Outdated

from __future__ import annotations

from collections.abc import Callable
from dataclasses import dataclass

from homeassistant.components.binary_sensor import (
DOMAIN as BINARY_SENSOR_DOMAIN,
BinarySensorEntity,
BinarySensorEntityDescription,
)
from homeassistant.const import EntityCategory
from homeassistant.core import HomeAssistant
from homeassistant.helpers.device_registry import DeviceEntryType, DeviceInfo
from homeassistant.helpers.dispatcher import async_dispatcher_connect
from homeassistant.helpers.entity_platform import AddConfigEntryEntitiesCallback

from .const import DOMAIN, SIGNAL_EVENTS_CHANGED
from .entity import Sun, SunConfigEntry

ENTITY_ID_BINARY_SENSOR_FORMAT = BINARY_SENSOR_DOMAIN + ".sun_{}"


@dataclass(kw_only=True, frozen=True)
class SunBinarySensorEntityDescription(BinarySensorEntityDescription):
"""Describes a Sun sensor entity."""
Comment thread
MartinHjelmare marked this conversation as resolved.
Outdated

value_fn: Callable[[Sun], bool | None]
signal: str


BINARY_SENSOR_TYPES: tuple[SunBinarySensorEntityDescription, ...] = (
SunBinarySensorEntityDescription(
key="solar_rising",
translation_key="solar_rising",
value_fn=lambda data: data.rising,
entity_registry_enabled_default=False,
signal=SIGNAL_EVENTS_CHANGED,
),
)


async def async_setup_entry(
hass: HomeAssistant,
entry: SunConfigEntry,
async_add_entities: AddConfigEntryEntitiesCallback,
) -> None:
"""Set up Sun binary sensor platform."""

sun = entry.runtime_data

async_add_entities(
[
SunBinarySensor(sun, description, entry.entry_id)
for description in BINARY_SENSOR_TYPES
]
)


class SunBinarySensor(BinarySensorEntity):
"""Representation of a Sun Sensor."""
Comment thread
MartinHjelmare marked this conversation as resolved.
Outdated

_attr_has_entity_name = True
_attr_should_poll = False
_attr_entity_category = EntityCategory.DIAGNOSTIC
entity_description: SunBinarySensorEntityDescription

def __init__(
self,
sun: Sun,
entity_description: SunBinarySensorEntityDescription,
entry_id: str,
) -> None:
"""Initiate Sun Binary Sensor."""
self.entity_description = entity_description
self.entity_id = ENTITY_ID_BINARY_SENSOR_FORMAT.format(entity_description.key)
self._attr_unique_id = f"{entry_id}-binary-{entity_description.key}"
Comment thread
MartinHjelmare marked this conversation as resolved.
Outdated
self.sun = sun
self._attr_device_info = DeviceInfo(
name="Sun",
identifiers={(DOMAIN, entry_id)},
entry_type=DeviceEntryType.SERVICE,
)

@property
def is_on(self) -> bool | None:
"""Return value of binary sensor."""
return self.entity_description.value_fn(self.sun)

async def async_added_to_hass(self) -> None:
"""Register signal listener when added to hass."""
await super().async_added_to_hass()
self.async_on_remove(
async_dispatcher_connect(
self.hass,
self.entity_description.signal,
self.async_write_ha_state,
)
)
9 changes: 9 additions & 0 deletions homeassistant/components/sun/icons.json
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,15 @@
"solar_rising": {
"default": "mdi:sun-clock"
}
},
"binary_sensor": {
"solar_rising": {
"default": "mdi:weather-sunny-off",
"state": {
"on": "mdi:weather-sunset-up",
"off": "mdi:weather-sunset-down"
}
}
}
}
}
9 changes: 9 additions & 0 deletions homeassistant/components/sun/strings.json
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,15 @@
"solar_azimuth": { "name": "Solar azimuth" },
"solar_elevation": { "name": "Solar elevation" },
"solar_rising": { "name": "Solar rising" }
},
"binary_sensor": {
"solar_rising": {
"name": "Solar rising",
"state": {
"on": "Rising",
"off": "Falling"
Comment thread
karwosts marked this conversation as resolved.
Outdated
}
}
}
}
}
44 changes: 44 additions & 0 deletions tests/components/sun/test_binary_sensor.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
"""The tests for the Sun binary_sensor platform."""

from datetime import datetime, timedelta

from freezegun.api import FrozenDateTimeFactory
import pytest

from homeassistant.components import sun
from homeassistant.const import EntityCategory
from homeassistant.core import HomeAssistant
from homeassistant.helpers import entity_registry as er
from homeassistant.setup import async_setup_component
from homeassistant.util import dt as dt_util


@pytest.mark.usefixtures("entity_registry_enabled_by_default")
async def test_setting_rising(
hass: HomeAssistant,
entity_registry: er.EntityRegistry,
freezer: FrozenDateTimeFactory,
) -> None:
"""Test retrieving sun setting and rising."""
utc_now = datetime(2016, 11, 1, 8, 0, 0, tzinfo=dt_util.UTC)
freezer.move_to(utc_now)
await async_setup_component(hass, sun.DOMAIN, {sun.DOMAIN: {}})
await hass.async_block_till_done()

assert hass.states.get("binary_sensor.sun_solar_rising").state == "on"

entry_ids = hass.config_entries.async_entries("sun")

freezer.tick(timedelta(hours=12))
# Block once for Sun to update
await hass.async_block_till_done()
# Block another time for the sensors to update
await hass.async_block_till_done()

# Make sure all the signals work
assert hass.states.get("binary_sensor.sun_solar_rising").state == "off"

entity = entity_registry.async_get("binary_sensor.sun_solar_rising")
assert entity
assert entity.entity_category is EntityCategory.DIAGNOSTIC
assert entity.unique_id == f"{entry_ids[0].entry_id}-binary-solar_rising"