mirror of
https://github.com/home-assistant/core.git
synced 2025-04-25 17:57:55 +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)
|
DEFAULT_TIME = datetime.time(0, 0, 0)
|
||||||
|
|
||||||
ATTR_DATETIME = "datetime"
|
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 = "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_KEY = DOMAIN
|
||||||
STORAGE_VERSION = 1
|
STORAGE_VERSION = 1
|
||||||
|
|
||||||
@ -138,37 +164,29 @@ async def async_setup(hass: HomeAssistantType, config: ConfigType) -> bool:
|
|||||||
|
|
||||||
async def async_set_datetime_service(entity, call):
|
async def async_set_datetime_service(entity, call):
|
||||||
"""Handle a call to the input datetime 'set datetime' service."""
|
"""Handle a call to the input datetime 'set datetime' service."""
|
||||||
time = call.data.get(ATTR_TIME)
|
|
||||||
date = call.data.get(ATTR_DATE)
|
date = call.data.get(ATTR_DATE)
|
||||||
|
time = call.data.get(ATTR_TIME)
|
||||||
dttm = call.data.get(ATTR_DATETIME)
|
dttm = call.data.get(ATTR_DATETIME)
|
||||||
if (
|
tmsp = call.data.get(ATTR_TIMESTAMP)
|
||||||
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
|
|
||||||
|
|
||||||
|
if tmsp:
|
||||||
|
dttm = dt_util.as_local(dt_util.utc_from_timestamp(tmsp)).replace(
|
||||||
|
tzinfo=None
|
||||||
|
)
|
||||||
if dttm:
|
if dttm:
|
||||||
date = dttm.date()
|
date = dttm.date()
|
||||||
time = dttm.time()
|
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)
|
entity.async_set_datetime(date, time)
|
||||||
|
|
||||||
component.async_register_entity_service(
|
component.async_register_entity_service(
|
||||||
SERVICE_SET_DATETIME,
|
SERVICE_SET_DATETIME, SERVICE_SET_DATETIME_SCHEMA, async_set_datetime_service
|
||||||
{
|
|
||||||
vol.Optional(ATTR_DATE): cv.date,
|
|
||||||
vol.Optional(ATTR_TIME): cv.time,
|
|
||||||
vol.Optional(ATTR_DATETIME): cv.datetime,
|
|
||||||
},
|
|
||||||
async_set_datetime_service,
|
|
||||||
)
|
)
|
||||||
|
|
||||||
return True
|
return True
|
||||||
@ -338,17 +356,11 @@ class InputDatetime(RestoreEntity):
|
|||||||
@callback
|
@callback
|
||||||
def async_set_datetime(self, date_val, time_val):
|
def async_set_datetime(self, date_val, time_val):
|
||||||
"""Set a new date / time."""
|
"""Set a new date / time."""
|
||||||
if self.has_date and self.has_time and date_val and 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._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
|
|
||||||
)
|
|
||||||
|
|
||||||
self.async_write_ha_state()
|
self.async_write_ha_state()
|
||||||
|
|
||||||
async def async_update_config(self, config: typing.Dict) -> None:
|
async def async_update_config(self, config: typing.Dict) -> None:
|
||||||
|
@ -1,18 +1,21 @@
|
|||||||
set_datetime:
|
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:
|
fields:
|
||||||
entity_id:
|
entity_id:
|
||||||
description: Entity id of the input datetime to set the new value.
|
description: Entity id of the input datetime to set the new value.
|
||||||
example: input_datetime.test_date_time
|
example: input_datetime.test_date_time
|
||||||
date:
|
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"'
|
example: '"2019-04-20"'
|
||||||
time:
|
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"'
|
example: '"05:04:20"'
|
||||||
datetime:
|
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"'
|
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:
|
reload:
|
||||||
description: Reload the input_datetime configuration.
|
description: Reload the input_datetime configuration.
|
||||||
|
@ -10,6 +10,7 @@ from homeassistant.components.input_datetime import (
|
|||||||
ATTR_DATETIME,
|
ATTR_DATETIME,
|
||||||
ATTR_EDITABLE,
|
ATTR_EDITABLE,
|
||||||
ATTR_TIME,
|
ATTR_TIME,
|
||||||
|
ATTR_TIMESTAMP,
|
||||||
CONF_HAS_DATE,
|
CONF_HAS_DATE,
|
||||||
CONF_HAS_TIME,
|
CONF_HAS_TIME,
|
||||||
CONF_ID,
|
CONF_ID,
|
||||||
@ -25,6 +26,7 @@ from homeassistant.core import Context, CoreState, State
|
|||||||
from homeassistant.exceptions import Unauthorized
|
from homeassistant.exceptions import Unauthorized
|
||||||
from homeassistant.helpers import entity_registry
|
from homeassistant.helpers import entity_registry
|
||||||
from homeassistant.setup import async_setup_component
|
from homeassistant.setup import async_setup_component
|
||||||
|
from homeassistant.util import dt as dt_util
|
||||||
|
|
||||||
from tests.async_mock import patch
|
from tests.async_mock import patch
|
||||||
from tests.common import mock_restore_cache
|
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):
|
async def test_invalid_configs(hass):
|
||||||
"""Test config."""
|
"""Test config."""
|
||||||
invalid_configs = [
|
invalid_configs = [
|
||||||
@ -156,6 +168,32 @@ async def test_set_datetime_2(hass):
|
|||||||
assert state.attributes["timestamp"] == dt_obj.timestamp()
|
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):
|
async def test_set_datetime_time(hass):
|
||||||
"""Test set_datetime method with only time."""
|
"""Test set_datetime method with only time."""
|
||||||
await async_setup_component(
|
await async_setup_component(
|
||||||
@ -199,7 +237,8 @@ async def test_set_invalid(hass):
|
|||||||
await hass.services.async_call(
|
await hass.services.async_call(
|
||||||
"input_datetime",
|
"input_datetime",
|
||||||
"set_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()
|
await hass.async_block_till_done()
|
||||||
|
|
||||||
@ -229,7 +268,8 @@ async def test_set_invalid_2(hass):
|
|||||||
await hass.services.async_call(
|
await hass.services.async_call(
|
||||||
"input_datetime",
|
"input_datetime",
|
||||||
"set_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()
|
await hass.async_block_till_done()
|
||||||
|
|
||||||
@ -358,8 +398,8 @@ async def test_input_datetime_context(hass, hass_admin_user):
|
|||||||
"input_datetime",
|
"input_datetime",
|
||||||
"set_datetime",
|
"set_datetime",
|
||||||
{"entity_id": state.entity_id, "date": "2018-01-02"},
|
{"entity_id": state.entity_id, "date": "2018-01-02"},
|
||||||
True,
|
blocking=True,
|
||||||
Context(user_id=hass_admin_user.id),
|
context=Context(user_id=hass_admin_user.id),
|
||||||
)
|
)
|
||||||
|
|
||||||
state2 = hass.states.get("input_datetime.only_date")
|
state2 = hass.states.get("input_datetime.only_date")
|
||||||
|
Loading…
x
Reference in New Issue
Block a user