mirror of
https://github.com/home-assistant/core.git
synced 2025-04-24 17:27:52 +00:00
Add timestamp option for input_datetime.set_datetime (#39121)
This commit is contained in:
parent
2568932c1c
commit
4ff376cdd6
@ -37,8 +37,34 @@ DEFAULT_DATE = datetime.date(1970, 1, 1)
|
||||
DEFAULT_TIME = datetime.time(0, 0, 0)
|
||||
|
||||
ATTR_DATETIME = "datetime"
|
||||
ATTR_TIMESTAMP = "timestamp"
|
||||
|
||||
|
||||
def validate_set_datetime_attrs(config):
|
||||
"""Validate set_datetime service attributes."""
|
||||
has_date_or_time_attr = any(key in config for key in (ATTR_DATE, ATTR_TIME))
|
||||
if (
|
||||
sum([has_date_or_time_attr, ATTR_DATETIME in config, ATTR_TIMESTAMP in config])
|
||||
> 1
|
||||
):
|
||||
raise vol.Invalid(f"Cannot use together: {', '.join(config.keys())}")
|
||||
return config
|
||||
|
||||
|
||||
SERVICE_SET_DATETIME = "set_datetime"
|
||||
SERVICE_SET_DATETIME_SCHEMA = vol.All(
|
||||
vol.Schema(
|
||||
{
|
||||
vol.Optional(ATTR_DATE): cv.date,
|
||||
vol.Optional(ATTR_TIME): cv.time,
|
||||
vol.Optional(ATTR_DATETIME): cv.datetime,
|
||||
vol.Optional(ATTR_TIMESTAMP): vol.Coerce(float),
|
||||
},
|
||||
extra=vol.ALLOW_EXTRA,
|
||||
),
|
||||
cv.has_at_least_one_key(ATTR_DATE, ATTR_TIME, ATTR_DATETIME, ATTR_TIMESTAMP),
|
||||
validate_set_datetime_attrs,
|
||||
)
|
||||
STORAGE_KEY = DOMAIN
|
||||
STORAGE_VERSION = 1
|
||||
|
||||
@ -138,37 +164,29 @@ async def async_setup(hass: HomeAssistantType, config: ConfigType) -> bool:
|
||||
|
||||
async def async_set_datetime_service(entity, call):
|
||||
"""Handle a call to the input datetime 'set datetime' service."""
|
||||
time = call.data.get(ATTR_TIME)
|
||||
date = call.data.get(ATTR_DATE)
|
||||
time = call.data.get(ATTR_TIME)
|
||||
dttm = call.data.get(ATTR_DATETIME)
|
||||
if (
|
||||
dttm
|
||||
and (date or time)
|
||||
or entity.has_date
|
||||
and not (date or dttm)
|
||||
or entity.has_time
|
||||
and not (time or dttm)
|
||||
):
|
||||
_LOGGER.error(
|
||||
"Invalid service data for %s input_datetime.set_datetime: %s",
|
||||
entity.entity_id,
|
||||
str(call.data),
|
||||
)
|
||||
return
|
||||
tmsp = call.data.get(ATTR_TIMESTAMP)
|
||||
|
||||
if tmsp:
|
||||
dttm = dt_util.as_local(dt_util.utc_from_timestamp(tmsp)).replace(
|
||||
tzinfo=None
|
||||
)
|
||||
if dttm:
|
||||
date = dttm.date()
|
||||
time = dttm.time()
|
||||
if not entity.has_date:
|
||||
date = None
|
||||
if not entity.has_time:
|
||||
time = None
|
||||
if not date and not time:
|
||||
raise vol.Invalid("Nothing to set")
|
||||
|
||||
entity.async_set_datetime(date, time)
|
||||
|
||||
component.async_register_entity_service(
|
||||
SERVICE_SET_DATETIME,
|
||||
{
|
||||
vol.Optional(ATTR_DATE): cv.date,
|
||||
vol.Optional(ATTR_TIME): cv.time,
|
||||
vol.Optional(ATTR_DATETIME): cv.datetime,
|
||||
},
|
||||
async_set_datetime_service,
|
||||
SERVICE_SET_DATETIME, SERVICE_SET_DATETIME_SCHEMA, async_set_datetime_service
|
||||
)
|
||||
|
||||
return True
|
||||
@ -338,17 +356,11 @@ class InputDatetime(RestoreEntity):
|
||||
@callback
|
||||
def async_set_datetime(self, date_val, time_val):
|
||||
"""Set a new date / time."""
|
||||
if self.has_date and self.has_time and date_val and time_val:
|
||||
self._current_datetime = datetime.datetime.combine(date_val, time_val)
|
||||
elif self.has_date and not self.has_time and date_val:
|
||||
self._current_datetime = datetime.datetime.combine(
|
||||
date_val, self._current_datetime.time()
|
||||
)
|
||||
if self.has_time and not self.has_date and time_val:
|
||||
self._current_datetime = datetime.datetime.combine(
|
||||
self._current_datetime.date(), time_val
|
||||
)
|
||||
|
||||
if not date_val:
|
||||
date_val = self._current_datetime.date()
|
||||
if not time_val:
|
||||
time_val = self._current_datetime.time()
|
||||
self._current_datetime = datetime.datetime.combine(date_val, time_val)
|
||||
self.async_write_ha_state()
|
||||
|
||||
async def async_update_config(self, config: typing.Dict) -> None:
|
||||
|
@ -1,18 +1,21 @@
|
||||
set_datetime:
|
||||
description: This can be used to dynamically set the date and/or time.
|
||||
description: This can be used to dynamically set the date and/or time. Use date/time, datetime or timestamp.
|
||||
fields:
|
||||
entity_id:
|
||||
description: Entity id of the input datetime to set the new value.
|
||||
example: input_datetime.test_date_time
|
||||
date:
|
||||
description: The target date the entity should be set to. Do not use with datetime.
|
||||
description: The target date the entity should be set to.
|
||||
example: '"2019-04-20"'
|
||||
time:
|
||||
description: The target time the entity should be set to. Do not use with datetime.
|
||||
description: The target time the entity should be set to.
|
||||
example: '"05:04:20"'
|
||||
datetime:
|
||||
description: The target date & time the entity should be set to. Do not use with date or time.
|
||||
description: The target date & time the entity should be set to.
|
||||
example: '"2019-04-20 05:04:20"'
|
||||
timestamp:
|
||||
description: The target date & time the entity should be set to as expressed by a UNIX timestamp.
|
||||
example: 1598027400
|
||||
|
||||
reload:
|
||||
description: Reload the input_datetime configuration.
|
||||
|
@ -10,6 +10,7 @@ from homeassistant.components.input_datetime import (
|
||||
ATTR_DATETIME,
|
||||
ATTR_EDITABLE,
|
||||
ATTR_TIME,
|
||||
ATTR_TIMESTAMP,
|
||||
CONF_HAS_DATE,
|
||||
CONF_HAS_TIME,
|
||||
CONF_ID,
|
||||
@ -25,6 +26,7 @@ from homeassistant.core import Context, CoreState, State
|
||||
from homeassistant.exceptions import Unauthorized
|
||||
from homeassistant.helpers import entity_registry
|
||||
from homeassistant.setup import async_setup_component
|
||||
from homeassistant.util import dt as dt_util
|
||||
|
||||
from tests.async_mock import patch
|
||||
from tests.common import mock_restore_cache
|
||||
@ -92,6 +94,16 @@ async def async_set_datetime(hass, entity_id, dt_value):
|
||||
)
|
||||
|
||||
|
||||
async def async_set_timestamp(hass, entity_id, timestamp):
|
||||
"""Set date and / or time of input_datetime."""
|
||||
await hass.services.async_call(
|
||||
DOMAIN,
|
||||
SERVICE_SET_DATETIME,
|
||||
{ATTR_ENTITY_ID: entity_id, ATTR_TIMESTAMP: timestamp},
|
||||
blocking=True,
|
||||
)
|
||||
|
||||
|
||||
async def test_invalid_configs(hass):
|
||||
"""Test config."""
|
||||
invalid_configs = [
|
||||
@ -156,6 +168,32 @@ async def test_set_datetime_2(hass):
|
||||
assert state.attributes["timestamp"] == dt_obj.timestamp()
|
||||
|
||||
|
||||
async def test_set_datetime_3(hass):
|
||||
"""Test set_datetime method using timestamp."""
|
||||
await async_setup_component(
|
||||
hass, DOMAIN, {DOMAIN: {"test_datetime": {"has_time": True, "has_date": True}}}
|
||||
)
|
||||
|
||||
entity_id = "input_datetime.test_datetime"
|
||||
|
||||
dt_obj = datetime.datetime(2017, 9, 7, 19, 46, 30)
|
||||
|
||||
await async_set_timestamp(hass, entity_id, dt_util.as_utc(dt_obj).timestamp())
|
||||
|
||||
state = hass.states.get(entity_id)
|
||||
assert state.state == str(dt_obj)
|
||||
assert state.attributes["has_time"]
|
||||
assert state.attributes["has_date"]
|
||||
|
||||
assert state.attributes["year"] == 2017
|
||||
assert state.attributes["month"] == 9
|
||||
assert state.attributes["day"] == 7
|
||||
assert state.attributes["hour"] == 19
|
||||
assert state.attributes["minute"] == 46
|
||||
assert state.attributes["second"] == 30
|
||||
assert state.attributes["timestamp"] == dt_obj.timestamp()
|
||||
|
||||
|
||||
async def test_set_datetime_time(hass):
|
||||
"""Test set_datetime method with only time."""
|
||||
await async_setup_component(
|
||||
@ -199,7 +237,8 @@ async def test_set_invalid(hass):
|
||||
await hass.services.async_call(
|
||||
"input_datetime",
|
||||
"set_datetime",
|
||||
{"entity_id": "test_date", "time": time_portion},
|
||||
{"entity_id": entity_id, "time": time_portion},
|
||||
blocking=True,
|
||||
)
|
||||
await hass.async_block_till_done()
|
||||
|
||||
@ -229,7 +268,8 @@ async def test_set_invalid_2(hass):
|
||||
await hass.services.async_call(
|
||||
"input_datetime",
|
||||
"set_datetime",
|
||||
{"entity_id": "test_date", "time": time_portion, "datetime": dt_obj},
|
||||
{"entity_id": entity_id, "time": time_portion, "datetime": dt_obj},
|
||||
blocking=True,
|
||||
)
|
||||
await hass.async_block_till_done()
|
||||
|
||||
@ -358,8 +398,8 @@ async def test_input_datetime_context(hass, hass_admin_user):
|
||||
"input_datetime",
|
||||
"set_datetime",
|
||||
{"entity_id": state.entity_id, "date": "2018-01-02"},
|
||||
True,
|
||||
Context(user_id=hass_admin_user.id),
|
||||
blocking=True,
|
||||
context=Context(user_id=hass_admin_user.id),
|
||||
)
|
||||
|
||||
state2 = hass.states.get("input_datetime.only_date")
|
||||
|
Loading…
x
Reference in New Issue
Block a user