Add `date` platform (#81948)
parent
f95f2d1cfc
commit
d138bbe26e
|
@ -234,6 +234,8 @@ build.json @home-assistant/supervisor
|
|||
/homeassistant/components/cups/ @fabaff
|
||||
/homeassistant/components/daikin/ @fredrike
|
||||
/tests/components/daikin/ @fredrike
|
||||
/homeassistant/components/date/ @home-assistant/core
|
||||
/tests/components/date/ @home-assistant/core
|
||||
/homeassistant/components/debugpy/ @frenck
|
||||
/tests/components/debugpy/ @frenck
|
||||
/homeassistant/components/deconz/ @Kane610
|
||||
|
|
|
@ -0,0 +1,109 @@
|
|||
"""Component to allow setting date as platforms."""
|
||||
from __future__ import annotations
|
||||
|
||||
from dataclasses import dataclass
|
||||
from datetime import date, timedelta
|
||||
import logging
|
||||
from typing import final
|
||||
|
||||
import voluptuous as vol
|
||||
|
||||
from homeassistant.config_entries import ConfigEntry
|
||||
from homeassistant.const import ATTR_DATE
|
||||
from homeassistant.core import HomeAssistant, ServiceCall
|
||||
from homeassistant.helpers import config_validation as cv
|
||||
from homeassistant.helpers.config_validation import ( # noqa: F401
|
||||
PLATFORM_SCHEMA,
|
||||
PLATFORM_SCHEMA_BASE,
|
||||
)
|
||||
from homeassistant.helpers.entity import Entity, EntityDescription
|
||||
from homeassistant.helpers.entity_component import EntityComponent
|
||||
from homeassistant.helpers.typing import ConfigType
|
||||
|
||||
from .const import DOMAIN, SERVICE_SET_VALUE
|
||||
|
||||
SCAN_INTERVAL = timedelta(seconds=30)
|
||||
|
||||
ENTITY_ID_FORMAT = DOMAIN + ".{}"
|
||||
|
||||
_LOGGER = logging.getLogger(__name__)
|
||||
|
||||
__all__ = ["DOMAIN", "DateEntity", "DateEntityDescription"]
|
||||
|
||||
|
||||
async def _async_set_value(entity: DateEntity, service_call: ServiceCall) -> None:
|
||||
"""Service call wrapper to set a new date."""
|
||||
return await entity.async_set_value(service_call.data[ATTR_DATE])
|
||||
|
||||
|
||||
async def async_setup(hass: HomeAssistant, config: ConfigType) -> bool:
|
||||
"""Set up Date entities."""
|
||||
component = hass.data[DOMAIN] = EntityComponent[DateEntity](
|
||||
_LOGGER, DOMAIN, hass, SCAN_INTERVAL
|
||||
)
|
||||
await component.async_setup(config)
|
||||
|
||||
component.async_register_entity_service(
|
||||
SERVICE_SET_VALUE, {vol.Required(ATTR_DATE): cv.date}, _async_set_value
|
||||
)
|
||||
|
||||
return True
|
||||
|
||||
|
||||
async def async_setup_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool:
|
||||
"""Set up a config entry."""
|
||||
component: EntityComponent[DateEntity] = hass.data[DOMAIN]
|
||||
return await component.async_setup_entry(entry)
|
||||
|
||||
|
||||
async def async_unload_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool:
|
||||
"""Unload a config entry."""
|
||||
component: EntityComponent[DateEntity] = hass.data[DOMAIN]
|
||||
return await component.async_unload_entry(entry)
|
||||
|
||||
|
||||
@dataclass
|
||||
class DateEntityDescription(EntityDescription):
|
||||
"""A class that describes date entities."""
|
||||
|
||||
|
||||
class DateEntity(Entity):
|
||||
"""Representation of a Date entity."""
|
||||
|
||||
entity_description: DateEntityDescription
|
||||
_attr_device_class: None
|
||||
_attr_native_value: date | None
|
||||
_attr_state: None = None
|
||||
|
||||
@property
|
||||
@final
|
||||
def device_class(self) -> None:
|
||||
"""Return the device class for the entity."""
|
||||
return None
|
||||
|
||||
@property
|
||||
@final
|
||||
def state_attributes(self) -> None:
|
||||
"""Return the state attributes."""
|
||||
return None
|
||||
|
||||
@property
|
||||
@final
|
||||
def state(self) -> str | None:
|
||||
"""Return the entity state."""
|
||||
if self.native_value is None:
|
||||
return None
|
||||
return self.native_value.isoformat()
|
||||
|
||||
@property
|
||||
def native_value(self) -> date | None:
|
||||
"""Return the value reported by the date."""
|
||||
return self._attr_native_value
|
||||
|
||||
def set_value(self, value: date) -> None:
|
||||
"""Change the date."""
|
||||
raise NotImplementedError()
|
||||
|
||||
async def async_set_value(self, value: date) -> None:
|
||||
"""Change the date."""
|
||||
await self.hass.async_add_executor_job(self.set_value, value)
|
|
@ -0,0 +1,5 @@
|
|||
"""Provides the constants needed for the component."""
|
||||
|
||||
DOMAIN = "date"
|
||||
|
||||
SERVICE_SET_VALUE = "set_value"
|
|
@ -0,0 +1,8 @@
|
|||
{
|
||||
"domain": "date",
|
||||
"name": "Date",
|
||||
"codeowners": ["@home-assistant/core"],
|
||||
"documentation": "https://www.home-assistant.io/integrations/date",
|
||||
"integration_type": "entity",
|
||||
"quality_scale": "internal"
|
||||
}
|
|
@ -0,0 +1,14 @@
|
|||
set_value:
|
||||
name: Set Date
|
||||
description: Set the date for a date entity.
|
||||
target:
|
||||
entity:
|
||||
domain: date
|
||||
fields:
|
||||
date:
|
||||
name: Date
|
||||
description: The date to set.
|
||||
required: true
|
||||
example: "2022/11/01"
|
||||
selector:
|
||||
date:
|
|
@ -0,0 +1,8 @@
|
|||
{
|
||||
"title": "Date",
|
||||
"entity_component": {
|
||||
"_": {
|
||||
"name": "[%key:component::date::title%]"
|
||||
}
|
||||
}
|
||||
}
|
|
@ -27,6 +27,7 @@ COMPONENTS_WITH_CONFIG_ENTRY_DEMO_PLATFORM = [
|
|||
Platform.CAMERA,
|
||||
Platform.CLIMATE,
|
||||
Platform.COVER,
|
||||
Platform.DATE,
|
||||
Platform.FAN,
|
||||
Platform.HUMIDIFIER,
|
||||
Platform.LIGHT,
|
||||
|
|
|
@ -0,0 +1,73 @@
|
|||
"""Demo platform that offers a fake Date entity."""
|
||||
from __future__ import annotations
|
||||
|
||||
from datetime import date
|
||||
|
||||
from homeassistant.components.date import DateEntity
|
||||
from homeassistant.config_entries import ConfigEntry
|
||||
from homeassistant.const import DEVICE_DEFAULT_NAME
|
||||
from homeassistant.core import HomeAssistant
|
||||
from homeassistant.helpers.entity import DeviceInfo
|
||||
from homeassistant.helpers.entity_platform import AddEntitiesCallback
|
||||
from homeassistant.helpers.typing import ConfigType, DiscoveryInfoType
|
||||
|
||||
from . import DOMAIN
|
||||
|
||||
|
||||
async def async_setup_platform(
|
||||
hass: HomeAssistant,
|
||||
config: ConfigType,
|
||||
async_add_entities: AddEntitiesCallback,
|
||||
discovery_info: DiscoveryInfoType | None = None,
|
||||
) -> None:
|
||||
"""Set up the Demo date entity."""
|
||||
async_add_entities(
|
||||
[
|
||||
DemoDate(
|
||||
"date",
|
||||
"Date",
|
||||
date(2020, 1, 1),
|
||||
"mdi:calendar",
|
||||
False,
|
||||
),
|
||||
]
|
||||
)
|
||||
|
||||
|
||||
async def async_setup_entry(
|
||||
hass: HomeAssistant,
|
||||
config_entry: ConfigEntry,
|
||||
async_add_entities: AddEntitiesCallback,
|
||||
) -> None:
|
||||
"""Set up the Demo config entry."""
|
||||
await async_setup_platform(hass, {}, async_add_entities)
|
||||
|
||||
|
||||
class DemoDate(DateEntity):
|
||||
"""Representation of a Demo date entity."""
|
||||
|
||||
_attr_should_poll = False
|
||||
|
||||
def __init__(
|
||||
self,
|
||||
unique_id: str,
|
||||
name: str,
|
||||
state: date,
|
||||
icon: str,
|
||||
assumed_state: bool,
|
||||
) -> None:
|
||||
"""Initialize the Demo date entity."""
|
||||
self._attr_assumed_state = assumed_state
|
||||
self._attr_icon = icon
|
||||
self._attr_name = name or DEVICE_DEFAULT_NAME
|
||||
self._attr_native_value = state
|
||||
self._attr_unique_id = unique_id
|
||||
|
||||
self._attr_device_info = DeviceInfo(
|
||||
identifiers={(DOMAIN, unique_id)}, name=self.name
|
||||
)
|
||||
|
||||
async def async_set_value(self, value: date) -> None:
|
||||
"""Update the date."""
|
||||
self._attr_native_value = value
|
||||
self.async_write_ha_state()
|
|
@ -31,6 +31,7 @@ class Platform(StrEnum):
|
|||
CAMERA = "camera"
|
||||
CLIMATE = "climate"
|
||||
COVER = "cover"
|
||||
DATE = "date"
|
||||
DEVICE_TRACKER = "device_tracker"
|
||||
FAN = "fan"
|
||||
GEO_LOCATION = "geo_location"
|
||||
|
|
|
@ -0,0 +1 @@
|
|||
"""Tests for the date component."""
|
|
@ -0,0 +1,54 @@
|
|||
"""The tests for the date component."""
|
||||
from datetime import date
|
||||
|
||||
from homeassistant.components.date import DOMAIN, SERVICE_SET_VALUE, DateEntity
|
||||
from homeassistant.const import (
|
||||
ATTR_DATE,
|
||||
ATTR_ENTITY_ID,
|
||||
ATTR_FRIENDLY_NAME,
|
||||
CONF_PLATFORM,
|
||||
)
|
||||
from homeassistant.core import HomeAssistant
|
||||
from homeassistant.setup import async_setup_component
|
||||
|
||||
|
||||
class MockDateEntity(DateEntity):
|
||||
"""Mock date device to use in tests."""
|
||||
|
||||
_attr_name = "date"
|
||||
|
||||
def __init__(self, native_value=date(2020, 1, 1)) -> None:
|
||||
"""Initialize mock date entity."""
|
||||
self._attr_native_value = native_value
|
||||
|
||||
async def async_set_value(self, value: date) -> None:
|
||||
"""Set the value of the date."""
|
||||
self._attr_native_value = value
|
||||
|
||||
|
||||
async def test_date(hass: HomeAssistant, enable_custom_integrations: None) -> None:
|
||||
"""Test date entity."""
|
||||
platform = getattr(hass.components, f"test.{DOMAIN}")
|
||||
platform.init()
|
||||
|
||||
assert await async_setup_component(hass, DOMAIN, {DOMAIN: {CONF_PLATFORM: "test"}})
|
||||
await hass.async_block_till_done()
|
||||
|
||||
state = hass.states.get("date.test")
|
||||
assert state.state == "2020-01-01"
|
||||
assert state.attributes == {ATTR_FRIENDLY_NAME: "test"}
|
||||
|
||||
await hass.services.async_call(
|
||||
DOMAIN,
|
||||
SERVICE_SET_VALUE,
|
||||
{ATTR_DATE: date(2021, 1, 1), ATTR_ENTITY_ID: "date.test"},
|
||||
blocking=True,
|
||||
)
|
||||
await hass.async_block_till_done()
|
||||
|
||||
state = hass.states.get("date.test")
|
||||
assert state.state == "2021-01-01"
|
||||
|
||||
date_entity = MockDateEntity(native_value=None)
|
||||
assert date_entity.state is None
|
||||
assert date_entity.state_attributes is None
|
|
@ -0,0 +1,34 @@
|
|||
"""The tests for the demo date component."""
|
||||
import pytest
|
||||
|
||||
from homeassistant.components.date import ATTR_DATE, DOMAIN, SERVICE_SET_VALUE
|
||||
from homeassistant.const import ATTR_ENTITY_ID
|
||||
from homeassistant.core import HomeAssistant
|
||||
from homeassistant.setup import async_setup_component
|
||||
|
||||
ENTITY_DATE = "date.date"
|
||||
|
||||
|
||||
@pytest.fixture(autouse=True)
|
||||
async def setup_demo_date(hass: HomeAssistant) -> None:
|
||||
"""Initialize setup demo date."""
|
||||
assert await async_setup_component(hass, DOMAIN, {"date": {"platform": "demo"}})
|
||||
await hass.async_block_till_done()
|
||||
|
||||
|
||||
def test_setup_params(hass: HomeAssistant) -> None:
|
||||
"""Test the initial parameters."""
|
||||
state = hass.states.get(ENTITY_DATE)
|
||||
assert state.state == "2020-01-01"
|
||||
|
||||
|
||||
async def test_set_datetime(hass: HomeAssistant) -> None:
|
||||
"""Test set datetime service."""
|
||||
await hass.services.async_call(
|
||||
DOMAIN,
|
||||
SERVICE_SET_VALUE,
|
||||
{ATTR_ENTITY_ID: ENTITY_DATE, ATTR_DATE: "2021-02-03"},
|
||||
blocking=True,
|
||||
)
|
||||
state = hass.states.get(ENTITY_DATE)
|
||||
assert state.state == "2021-02-03"
|
|
@ -0,0 +1,50 @@
|
|||
"""Provide a mock date platform.
|
||||
|
||||
Call init before using it in your tests to ensure clean test data.
|
||||
"""
|
||||
from datetime import date
|
||||
|
||||
from homeassistant.components.date import DateEntity
|
||||
|
||||
from tests.common import MockEntity
|
||||
|
||||
UNIQUE_DATE = "unique_date"
|
||||
|
||||
ENTITIES = []
|
||||
|
||||
|
||||
class MockDateEntity(MockEntity, DateEntity):
|
||||
"""Mock date class."""
|
||||
|
||||
@property
|
||||
def native_value(self):
|
||||
"""Return the native value of this date."""
|
||||
return self._handle("native_value")
|
||||
|
||||
def set_value(self, value: date) -> None:
|
||||
"""Change the date."""
|
||||
self._values["native_value"] = value
|
||||
|
||||
|
||||
def init(empty=False):
|
||||
"""Initialize the platform with entities."""
|
||||
global ENTITIES
|
||||
|
||||
ENTITIES = (
|
||||
[]
|
||||
if empty
|
||||
else [
|
||||
MockDateEntity(
|
||||
name="test",
|
||||
unique_id=UNIQUE_DATE,
|
||||
native_value=date(2020, 1, 1),
|
||||
),
|
||||
]
|
||||
)
|
||||
|
||||
|
||||
async def async_setup_platform(
|
||||
hass, config, async_add_entities_callback, discovery_info=None
|
||||
):
|
||||
"""Return mock entities."""
|
||||
async_add_entities_callback(ENTITIES)
|
Loading…
Reference in New Issue