core/homeassistant/components/time_date/sensor.py

159 lines
5.1 KiB
Python
Raw Normal View History

"""Support for showing the date and the time."""
from datetime import timedelta
import logging
import voluptuous as vol
from homeassistant.components.sensor import PLATFORM_SCHEMA, SensorEntity
from homeassistant.const import CONF_DISPLAY_OPTIONS
from homeassistant.core import callback
import homeassistant.helpers.config_validation as cv
from homeassistant.helpers.event import async_track_point_in_utc_time
import homeassistant.util.dt as dt_util
_LOGGER = logging.getLogger(__name__)
2019-07-31 19:25:30 +00:00
TIME_STR_FORMAT = "%H:%M"
2015-05-08 14:59:46 +00:00
OPTION_TYPES = {
2019-07-31 19:25:30 +00:00
"time": "Time",
"date": "Date",
"date_time": "Date & Time",
"date_time_utc": "Date & Time (UTC)",
"date_time_iso": "Date & Time (ISO)",
2019-07-31 19:25:30 +00:00
"time_date": "Time & Date",
"beat": "Internet Time",
"time_utc": "Time (UTC)",
2015-05-08 14:59:46 +00:00
}
2019-07-31 19:25:30 +00:00
PLATFORM_SCHEMA = PLATFORM_SCHEMA.extend(
{
vol.Optional(CONF_DISPLAY_OPTIONS, default=["time"]): vol.All(
cv.ensure_list, [vol.In(OPTION_TYPES)]
)
}
)
2015-05-08 14:59:46 +00:00
2019-07-31 19:25:30 +00:00
async def async_setup_platform(hass, config, async_add_entities, discovery_info=None):
"""Set up the Time and Date sensor."""
2015-05-08 14:59:46 +00:00
if hass.config.time_zone is None:
_LOGGER.error("Timezone is not set in Home Assistant configuration")
2015-05-08 14:59:46 +00:00
return False
async_add_entities(
[TimeDateSensor(hass, variable) for variable in config[CONF_DISPLAY_OPTIONS]]
)
2015-05-08 14:59:46 +00:00
class TimeDateSensor(SensorEntity):
2016-03-08 15:46:34 +00:00
"""Implementation of a Time and Date sensor."""
2015-05-08 14:59:46 +00:00
def __init__(self, hass, option_type):
2016-03-08 15:46:34 +00:00
"""Initialize the sensor."""
self._name = OPTION_TYPES[option_type]
self.type = option_type
2015-05-08 14:59:46 +00:00
self._state = None
self.hass = hass
self.unsub = None
self._update_internal_state(dt_util.utcnow())
2015-05-08 14:59:46 +00:00
@property
def name(self):
2016-03-08 15:46:34 +00:00
"""Return the name of the sensor."""
2015-05-08 14:59:46 +00:00
return self._name
@property
def state(self):
2016-03-08 15:46:34 +00:00
"""Return the state of the sensor."""
2015-05-08 14:59:46 +00:00
return self._state
2016-02-04 20:55:22 +00:00
@property
def icon(self):
2016-02-23 05:21:49 +00:00
"""Icon to use in the frontend, if any."""
2019-07-31 19:25:30 +00:00
if "date" in self.type and "time" in self.type:
return "mdi:calendar-clock"
if "date" in self.type:
return "mdi:calendar"
return "mdi:clock"
2016-02-04 20:55:22 +00:00
async def async_added_to_hass(self) -> None:
2020-11-13 08:31:55 +00:00
"""Set up first update."""
self.unsub = async_track_point_in_utc_time(
self.hass, self.point_in_time_listener, self.get_next_interval()
)
async def async_will_remove_from_hass(self) -> None:
"""Cancel next update."""
if self.unsub:
self.unsub()
self.unsub = None
2020-11-13 08:31:55 +00:00
def get_next_interval(self):
"""Compute next time an update should occur."""
2020-11-13 08:31:55 +00:00
now = dt_util.utcnow()
2019-07-31 19:25:30 +00:00
if self.type == "date":
tomorrow = dt_util.as_local(now) + timedelta(days=1)
return dt_util.start_of_local_day(tomorrow)
2020-11-13 08:31:55 +00:00
2019-07-31 19:25:30 +00:00
if self.type == "beat":
# Add 1 hour because @0 beats is at 23:00:00 UTC.
timestamp = dt_util.as_timestamp(now + timedelta(hours=1))
interval = 86.4
else:
timestamp = dt_util.as_timestamp(now)
interval = 60
delta = interval - (timestamp % interval)
2020-11-13 08:31:55 +00:00
next_interval = now + timedelta(seconds=delta)
_LOGGER.debug("%s + %s -> %s (%s)", now, delta, next_interval, self.type)
return next_interval
def _update_internal_state(self, time_date):
2016-04-16 07:55:35 +00:00
time = dt_util.as_local(time_date).strftime(TIME_STR_FORMAT)
time_utc = time_date.strftime(TIME_STR_FORMAT)
date = dt_util.as_local(time_date).date().isoformat()
date_utc = time_date.date().isoformat()
2019-07-31 19:25:30 +00:00
if self.type == "time":
self._state = time
2019-07-31 19:25:30 +00:00
elif self.type == "date":
self._state = date
2019-07-31 19:25:30 +00:00
elif self.type == "date_time":
self._state = f"{date}, {time}"
elif self.type == "date_time_utc":
self._state = f"{date_utc}, {time_utc}"
2019-07-31 19:25:30 +00:00
elif self.type == "time_date":
self._state = f"{time}, {date}"
2019-07-31 19:25:30 +00:00
elif self.type == "time_utc":
self._state = time_utc
2019-07-31 19:25:30 +00:00
elif self.type == "beat":
2020-11-13 08:33:00 +00:00
# Calculate Swatch Internet Time.
time_bmt = time_date + timedelta(hours=1)
delta = timedelta(
hours=time_bmt.hour,
minutes=time_bmt.minute,
seconds=time_bmt.second,
microseconds=time_bmt.microsecond,
)
# Use integers to better handle rounding. For example,
# int(63763.2/86.4) = 737 but 637632//864 = 738.
beat = int(delta.total_seconds() * 10) // 864
self._state = f"@{beat:03d}"
2019-07-31 19:25:30 +00:00
elif self.type == "date_time_iso":
self._state = dt_util.parse_datetime(f"{date} {time}").isoformat()
@callback
def point_in_time_listener(self, time_date):
"""Get the latest data and update state."""
self._update_internal_state(time_date)
self.async_write_ha_state()
self.unsub = async_track_point_in_utc_time(
2019-07-31 19:25:30 +00:00
self.hass, self.point_in_time_listener, self.get_next_interval()
)