Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add config flow for Time & Date #104183

Merged
merged 20 commits into from
Jan 23, 2024
Merged
Show file tree
Hide file tree
Changes from 17 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
17 changes: 17 additions & 0 deletions homeassistant/components/time_date/__init__.py
Original file line number Diff line number Diff line change
@@ -1 +1,18 @@
"""The time_date component."""
from __future__ import annotations

from homeassistant.config_entries import ConfigEntry
from homeassistant.core import HomeAssistant

from .const import PLATFORMS


async def async_setup_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool:
"""Set up Time & Date from a config entry."""
await hass.config_entries.async_forward_entry_setups(entry, PLATFORMS)
return True


async def async_unload_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool:
"""Unload Time & Date config entry."""
return await hass.config_entries.async_unload_platforms(entry, PLATFORMS)
130 changes: 130 additions & 0 deletions homeassistant/components/time_date/config_flow.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,130 @@
"""Adds config flow for Time & Date integration."""
from __future__ import annotations

from collections.abc import Mapping
from datetime import timedelta
import logging
from typing import Any

import voluptuous as vol

from homeassistant.components import websocket_api
from homeassistant.components.sensor import DOMAIN as SENSOR_DOMAIN
from homeassistant.core import HomeAssistant, callback
from homeassistant.helpers.entity_platform import EntityPlatform
from homeassistant.helpers.schema_config_entry_flow import (
SchemaCommonFlowHandler,
SchemaConfigFlowHandler,
SchemaFlowError,
SchemaFlowFormStep,
)
from homeassistant.helpers.selector import (
SelectSelector,
SelectSelectorConfig,
SelectSelectorMode,
)
from homeassistant.setup import async_prepare_setup_platform

from .const import CONF_DISPLAY_OPTIONS, DOMAIN, OPTION_TYPES
from .sensor import TimeDateSensor

_LOGGER = logging.getLogger(__name__)

USER_SCHEMA = vol.Schema(
{
vol.Required(CONF_DISPLAY_OPTIONS): SelectSelector(
SelectSelectorConfig(
options=[option for option in OPTION_TYPES if option != "beat"],
mode=SelectSelectorMode.DROPDOWN,
translation_key="display_options",
)
),
}
)


async def validate_input(
handler: SchemaCommonFlowHandler, user_input: dict[str, Any]
) -> dict[str, Any]:
"""Validate rest setup."""
hass = handler.parent_handler.hass
if hass.config.time_zone is None:
raise SchemaFlowError("timezone_not_exist")
return user_input


CONFIG_FLOW = {
"user": SchemaFlowFormStep(
schema=USER_SCHEMA,
preview=DOMAIN,
validate_user_input=validate_input,
)
}


class TimeDateConfigFlowHandler(SchemaConfigFlowHandler, domain=DOMAIN):
"""Handle a config flow for Time & Date."""

config_flow = CONFIG_FLOW

def async_config_entry_title(self, options: Mapping[str, Any]) -> str:
"""Return config entry title."""
return f"Time & Date {options[CONF_DISPLAY_OPTIONS]}"
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This title should be translated, but maybe that's not possible?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Not possible to translate the config entry title as far as I know


def async_config_flow_finished(self, options: Mapping[str, Any]) -> None:
"""Abort if instance already exist."""
self._async_abort_entries_match(dict(options))

@staticmethod
async def async_setup_preview(hass: HomeAssistant) -> None:
"""Set up preview WS API."""
websocket_api.async_register_command(hass, ws_start_preview)


@websocket_api.websocket_command(
{
vol.Required("type"): "time_date/start_preview",
vol.Required("flow_id"): str,
vol.Required("flow_type"): vol.Any("config_flow"),
vol.Required("user_input"): dict,
}
)
@websocket_api.async_response
async def ws_start_preview(
hass: HomeAssistant,
connection: websocket_api.ActiveConnection,
msg: dict[str, Any],
) -> None:
"""Generate a preview."""
validated = USER_SCHEMA(msg["user_input"])

# Create an EntityPlatform, needed for name translations
platform = await async_prepare_setup_platform(hass, {}, SENSOR_DOMAIN, DOMAIN)
entity_platform = EntityPlatform(
hass=hass,
logger=_LOGGER,
domain=SENSOR_DOMAIN,
platform_name=DOMAIN,
platform=platform,
scan_interval=timedelta(seconds=3600),
entity_namespace=None,
)
await entity_platform.async_load_translations()

@callback
def async_preview_updated(state: str, attributes: Mapping[str, Any]) -> None:
"""Forward config entry state events to websocket."""
connection.send_message(
websocket_api.event_message(
msg["id"], {"attributes": attributes, "state": state}
)
)

preview_entity = TimeDateSensor(validated[CONF_DISPLAY_OPTIONS])
preview_entity.hass = hass
preview_entity.platform = entity_platform

connection.send_result(msg["id"])
connection.subscriptions[msg["id"]] = preview_entity.async_start_preview(
async_preview_updated
)
16 changes: 16 additions & 0 deletions homeassistant/components/time_date/const.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,4 +3,20 @@

from typing import Final

from homeassistant.const import Platform

CONF_DISPLAY_OPTIONS = "display_options"
DOMAIN: Final = "time_date"
PLATFORMS = [Platform.SENSOR]
TIME_STR_FORMAT = "%H:%M"

OPTION_TYPES = [
"time",
"date",
"date_time",
"date_time_utc",
"date_time_iso",
"time_date",
"beat",
"time_utc",
]
2 changes: 2 additions & 0 deletions homeassistant/components/time_date/manifest.json
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,9 @@
"domain": "time_date",
"name": "Time & Date",
"codeowners": ["@fabaff"],
"config_flow": true,
"documentation": "https://www.home-assistant.io/integrations/time_date",
"integration_type": "helper",
gjohansson-ST marked this conversation as resolved.
Show resolved Hide resolved
"iot_class": "local_push",
"quality_scale": "internal"
}
80 changes: 58 additions & 22 deletions homeassistant/components/time_date/sensor.py
Original file line number Diff line number Diff line change
@@ -1,12 +1,19 @@
"""Support for showing the date and the time."""
from __future__ import annotations

from collections.abc import Callable, Mapping
from datetime import datetime, timedelta
import logging
from typing import Any

import voluptuous as vol

from homeassistant.components.sensor import PLATFORM_SCHEMA, SensorEntity
from homeassistant.components.sensor import (
ENTITY_ID_FORMAT,
PLATFORM_SCHEMA,
SensorEntity,
)
from homeassistant.config_entries import ConfigEntry
from homeassistant.const import CONF_DISPLAY_OPTIONS, EVENT_CORE_CONFIG_UPDATE
from homeassistant.core import CALLBACK_TYPE, Event, HomeAssistant, callback
import homeassistant.helpers.config_validation as cv
Expand All @@ -16,22 +23,12 @@
from homeassistant.helpers.typing import ConfigType, DiscoveryInfoType
import homeassistant.util.dt as dt_util

from .const import DOMAIN
from .const import DOMAIN, OPTION_TYPES

_LOGGER = logging.getLogger(__name__)

TIME_STR_FORMAT = "%H:%M"

OPTION_TYPES = {
"time": "Time",
"date": "Date",
"date_time": "Date & Time",
"date_time_utc": "Date & Time (UTC)",
"date_time_iso": "Date & Time (ISO)",
"time_date": "Time & Date",
"beat": "Internet Time",
"time_utc": "Time (UTC)",
}

PLATFORM_SCHEMA = PLATFORM_SCHEMA.extend(
{
Expand Down Expand Up @@ -71,27 +68,37 @@ async def async_setup_platform(
_LOGGER.warning("'beat': is deprecated and will be removed in version 2024.7")

async_add_entities(
[TimeDateSensor(hass, variable) for variable in config[CONF_DISPLAY_OPTIONS]]
[TimeDateSensor(variable) for variable in config[CONF_DISPLAY_OPTIONS]]
)


async def async_setup_entry(
hass: HomeAssistant, entry: ConfigEntry, async_add_entities: AddEntitiesCallback
) -> None:
"""Set up the Time & Date sensor."""

async_add_entities(
[TimeDateSensor(entry.options[CONF_DISPLAY_OPTIONS], entry.entry_id)]
)


class TimeDateSensor(SensorEntity):
"""Implementation of a Time and Date sensor."""

_attr_should_poll = False
_attr_has_entity_name = True
_state: str | None = None
unsub: CALLBACK_TYPE | None = None

def __init__(self, hass: HomeAssistant, option_type: str) -> None:
def __init__(self, option_type: str, entry_id: str | None = None) -> None:
"""Initialize the sensor."""
self._name = OPTION_TYPES[option_type]
self._attr_translation_key = option_type
self.type = option_type
self._state: str | None = None
self.hass = hass
self.unsub: CALLBACK_TYPE | None = None
object_id = "internet_time" if option_type == "beat" else option_type
self.entity_id = ENTITY_ID_FORMAT.format(object_id)
self._attr_unique_id = option_type if entry_id else None

@property
def name(self) -> str:
"""Return the name of the sensor."""
return self._name
self._update_internal_state(dt_util.utcnow())

@property
def native_value(self) -> str | None:
Expand All @@ -107,6 +114,35 @@ def icon(self) -> str:
return "mdi:calendar"
return "mdi:clock"

@callback
def async_start_preview(
self,
preview_callback: Callable[[str, Mapping[str, Any]], None],
) -> CALLBACK_TYPE:
"""Render a preview."""

@callback
def point_in_time_listener(time_date: datetime | None) -> None:
"""Update preview."""

now = dt_util.utcnow()
self._update_internal_state(now)
self.unsub = async_track_point_in_utc_time(
self.hass, point_in_time_listener, self.get_next_interval(now)
)
calculated_state = self._async_calculate_state()
preview_callback(calculated_state.state, calculated_state.attributes)

@callback
def async_stop_preview() -> None:
"""Stop preview."""
if self.unsub:
self.unsub()
self.unsub = None

point_in_time_listener(None)
return async_stop_preview

async def async_added_to_hass(self) -> None:
"""Set up first update."""

Expand Down
76 changes: 75 additions & 1 deletion homeassistant/components/time_date/strings.json
Original file line number Diff line number Diff line change
@@ -1,8 +1,82 @@
{
"config": {
"abort": {
"already_configured": "Time & Date option type has already been configured"
},
"step": {
"user": {
"description": "Select your type below",
"data": {
"display_options": "Sensors"
}
}
},
"error": {
"timezone_not_exist": "Timezone is not set in Home Assistant configuration"
}
},
"options": {
"step": {
"init": {
"data": {
"display_options": "[%key:component::time_date::config::step::user::data::display_options%]"
}
}
}
},
"selector": {
"display_options": {
"options": {
"time": "Time",
"date": "Date",
"date_time": "Date & Time",
"date_time_utc": "Date & Time (UTC)",
"date_time_iso": "Date & Time (ISO)",
"time_date": "Time & Date",
"beat": "Internet time",
"time_utc": "Time (UTC)"
}
}
},
"entity": {
"sensor": {
"time": {
"name": "[%key:component::time_date::selector::display_options::options::time%]"
},
"date": {
"name": "[%key:component::time_date::selector::display_options::options::date%]"
},
"date_time": {
"name": "[%key:component::time_date::selector::display_options::options::date_time%]"
},
"date_time_utc": {
"name": "[%key:component::time_date::selector::display_options::options::date_time_utc%]"
},
"date_time_iso": {
"name": "[%key:component::time_date::selector::display_options::options::date_time_iso%]"
},
"time_date": {
"name": "[%key:component::time_date::selector::display_options::options::time_date%]"
},
"beat": {
"name": "[%key:component::time_date::selector::display_options::options::beat%]"
},
"time_utc": {
"name": "[%key:component::time_date::selector::display_options::options::time_utc%]"
}
}
},
"issues": {
"deprecated_beat": {
"title": "The `{config_key}` Time & Date sensor is being removed",
"description": "Please remove the `{config_key}` key from the `{display_options}` for the {integration} entry in your configuration.yaml file and restart Home Assistant to fix this issue."
"fix_flow": {
"step": {
"confirm": {
"title": "[%key:component::time_date::issues::deprecated_beat::title%]",
"description": "Please remove the `{config_key}` key from the {integration} config entry options and click submit to fix this issue."
}
}
}
}
}
}
Loading