-
-
Notifications
You must be signed in to change notification settings - Fork 32.3k
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
Showing
13 changed files
with
348 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,63 @@ | ||
"""Demo platform that offers a fake time entity.""" | ||
from __future__ import annotations | ||
|
||
from datetime import time | ||
|
||
from homeassistant.components.time import TimeEntity | ||
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 time entity.""" | ||
async_add_entities([DemoTime("time", "Time", time(12, 0, 0), "mdi:clock", 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 DemoTime(TimeEntity): | ||
"""Representation of a Demo time entity.""" | ||
|
||
_attr_should_poll = False | ||
|
||
def __init__( | ||
self, | ||
unique_id: str, | ||
name: str, | ||
state: time, | ||
icon: str, | ||
assumed_state: bool, | ||
) -> None: | ||
"""Initialize the Demo time 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: time) -> None: | ||
"""Update the time.""" | ||
self._attr_native_value = value | ||
self.async_write_ha_state() |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,109 @@ | ||
"""Component to allow setting time as platforms.""" | ||
from __future__ import annotations | ||
|
||
from dataclasses import dataclass | ||
from datetime import time, timedelta | ||
import logging | ||
from typing import final | ||
|
||
import voluptuous as vol | ||
|
||
from homeassistant.config_entries import ConfigEntry | ||
from homeassistant.const import ATTR_TIME | ||
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", "TimeEntity", "TimeEntityDescription"] | ||
|
||
|
||
async def _async_set_value(entity: TimeEntity, service_call: ServiceCall) -> None: | ||
"""Service call wrapper to set a new date.""" | ||
return await entity.async_set_value(service_call.data[ATTR_TIME]) | ||
|
||
|
||
async def async_setup(hass: HomeAssistant, config: ConfigType) -> bool: | ||
"""Set up Time entities.""" | ||
component = hass.data[DOMAIN] = EntityComponent[TimeEntity]( | ||
_LOGGER, DOMAIN, hass, SCAN_INTERVAL | ||
) | ||
await component.async_setup(config) | ||
|
||
component.async_register_entity_service( | ||
SERVICE_SET_VALUE, {vol.Required(ATTR_TIME): cv.time}, _async_set_value | ||
) | ||
|
||
return True | ||
|
||
|
||
async def async_setup_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool: | ||
"""Set up a config entry.""" | ||
component: EntityComponent[TimeEntity] = 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[TimeEntity] = hass.data[DOMAIN] | ||
return await component.async_unload_entry(entry) | ||
|
||
|
||
@dataclass | ||
class TimeEntityDescription(EntityDescription): | ||
"""A class that describes time entities.""" | ||
|
||
|
||
class TimeEntity(Entity): | ||
"""Representation of a Time entity.""" | ||
|
||
entity_description: TimeEntityDescription | ||
_attr_native_value: time | None | ||
_attr_device_class: None = 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) -> time | None: | ||
"""Return the value reported by the time.""" | ||
return self._attr_native_value | ||
|
||
def set_value(self, value: time) -> None: | ||
"""Change the time.""" | ||
raise NotImplementedError() | ||
|
||
async def async_set_value(self, value: time) -> None: | ||
"""Change the time.""" | ||
await self.hass.async_add_executor_job(self.set_value, value) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,5 @@ | ||
"""Provides the constants needed for the component.""" | ||
|
||
DOMAIN = "time" | ||
|
||
SERVICE_SET_VALUE = "set_value" |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,8 @@ | ||
{ | ||
"domain": "time", | ||
"name": "Time", | ||
"codeowners": ["@home-assistant/core"], | ||
"documentation": "https://www.home-assistant.io/integrations/time", | ||
"integration_type": "entity", | ||
"quality_scale": "internal" | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,14 @@ | ||
set_value: | ||
name: Set Time | ||
description: Set the time for a time entity. | ||
target: | ||
entity: | ||
domain: time | ||
fields: | ||
time: | ||
name: Time | ||
description: The time to set. | ||
required: true | ||
example: "22:15" | ||
selector: | ||
time: |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,8 @@ | ||
{ | ||
"title": "Time", | ||
"entity_component": { | ||
"_": { | ||
"name": "[%key:component::time::title%]" | ||
} | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,34 @@ | ||
"""The tests for the demo time component.""" | ||
import pytest | ||
|
||
from homeassistant.components.time import ATTR_TIME, DOMAIN, SERVICE_SET_VALUE | ||
from homeassistant.const import ATTR_ENTITY_ID | ||
from homeassistant.core import HomeAssistant | ||
from homeassistant.setup import async_setup_component | ||
|
||
ENTITY_TIME = "time.time" | ||
|
||
|
||
@pytest.fixture(autouse=True) | ||
async def setup_demo_datetime(hass: HomeAssistant) -> None: | ||
"""Initialize setup demo time.""" | ||
assert await async_setup_component(hass, DOMAIN, {"time": {"platform": "demo"}}) | ||
await hass.async_block_till_done() | ||
|
||
|
||
def test_setup_params(hass: HomeAssistant) -> None: | ||
"""Test the initial parameters.""" | ||
state = hass.states.get(ENTITY_TIME) | ||
assert state.state == "12:00:00" | ||
|
||
|
||
async def test_set_value(hass: HomeAssistant) -> None: | ||
"""Test set value service.""" | ||
await hass.services.async_call( | ||
DOMAIN, | ||
SERVICE_SET_VALUE, | ||
{ATTR_ENTITY_ID: ENTITY_TIME, ATTR_TIME: "01:02:03"}, | ||
blocking=True, | ||
) | ||
state = hass.states.get(ENTITY_TIME) | ||
assert state.state == "01:02:03" |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1 @@ | ||
"""Tests for the time component.""" |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,52 @@ | ||
"""The tests for the time component.""" | ||
from datetime import time | ||
|
||
from homeassistant.components.time import DOMAIN, SERVICE_SET_VALUE, TimeEntity | ||
from homeassistant.const import ( | ||
ATTR_ENTITY_ID, | ||
ATTR_FRIENDLY_NAME, | ||
ATTR_TIME, | ||
CONF_PLATFORM, | ||
) | ||
from homeassistant.core import HomeAssistant | ||
from homeassistant.setup import async_setup_component | ||
|
||
|
||
class MockTimeEntity(TimeEntity): | ||
"""Mock time device to use in tests.""" | ||
|
||
def __init__(self, native_value=time(12, 0, 0)) -> None: | ||
"""Initialize mock time entity.""" | ||
self._attr_native_value = native_value | ||
|
||
async def async_set_value(self, value: time) -> None: | ||
"""Set the value of the time.""" | ||
self._attr_native_value = value | ||
|
||
|
||
async def test_date(hass: HomeAssistant, enable_custom_integrations: None) -> None: | ||
"""Test time 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("time.test") | ||
assert state.state == "01:02:03" | ||
assert state.attributes == {ATTR_FRIENDLY_NAME: "test"} | ||
|
||
await hass.services.async_call( | ||
DOMAIN, | ||
SERVICE_SET_VALUE, | ||
{ATTR_TIME: time(2, 3, 4), ATTR_ENTITY_ID: "time.test"}, | ||
blocking=True, | ||
) | ||
await hass.async_block_till_done() | ||
|
||
state = hass.states.get("time.test") | ||
assert state.state == "02:03:04" | ||
|
||
date_entity = MockTimeEntity(native_value=None) | ||
assert date_entity.state is None | ||
assert date_entity.state_attributes is None |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,50 @@ | ||
"""Provide a mock time platform. | ||
Call init before using it in your tests to ensure clean test data. | ||
""" | ||
from datetime import time | ||
|
||
from homeassistant.components.time import TimeEntity | ||
|
||
from tests.common import MockEntity | ||
|
||
UNIQUE_TIME = "unique_time" | ||
|
||
ENTITIES = [] | ||
|
||
|
||
class MockTimeEntity(MockEntity, TimeEntity): | ||
"""Mock time class.""" | ||
|
||
@property | ||
def native_value(self): | ||
"""Return the native value of this time.""" | ||
return self._handle("native_value") | ||
|
||
def set_value(self, value: time) -> None: | ||
"""Change the time.""" | ||
self._values["native_value"] = value | ||
|
||
|
||
def init(empty=False): | ||
"""Initialize the platform with entities.""" | ||
global ENTITIES | ||
|
||
ENTITIES = ( | ||
[] | ||
if empty | ||
else [ | ||
MockTimeEntity( | ||
name="test", | ||
unique_id=UNIQUE_TIME, | ||
native_value=time(1, 2, 3), | ||
), | ||
] | ||
) | ||
|
||
|
||
async def async_setup_platform( | ||
hass, config, async_add_entities_callback, discovery_info=None | ||
): | ||
"""Return mock entities.""" | ||
async_add_entities_callback(ENTITIES) |