Skip to content

Commit f5e1644

Browse files
committed
Resync with ha-core
1 parent eeba352 commit f5e1644

File tree

5 files changed

+33
-248
lines changed

5 files changed

+33
-248
lines changed

custom_components/file_plusplus/__init__.py

Lines changed: 4 additions & 90 deletions
Original file line numberDiff line numberDiff line change
@@ -3,88 +3,16 @@
33
from copy import deepcopy
44
from typing import Any
55

6-
from homeassistant.components.notify import migrate_notify_issue
7-
from homeassistant.config_entries import SOURCE_IMPORT, ConfigEntry
8-
from homeassistant.const import (
9-
CONF_FILE_PATH,
10-
CONF_NAME,
11-
CONF_PLATFORM,
12-
CONF_SCAN_INTERVAL,
13-
Platform,
14-
)
15-
from homeassistant.core import DOMAIN as HOMEASSISTANT_DOMAIN, HomeAssistant
6+
from homeassistant.config_entries import ConfigEntry
7+
from homeassistant.const import CONF_FILE_PATH, CONF_NAME, CONF_PLATFORM, Platform
8+
from homeassistant.core import HomeAssistant
169
from homeassistant.exceptions import ConfigEntryNotReady
17-
from homeassistant.helpers import (
18-
config_validation as cv,
19-
discovery,
20-
issue_registry as ir,
21-
)
22-
from homeassistant.helpers.typing import ConfigType
2310

2411
from .const import DOMAIN
25-
from .notify import PLATFORM_SCHEMA as NOTIFY_PLATFORM_SCHEMA
26-
from .sensor import PLATFORM_SCHEMA as SENSOR_PLATFORM_SCHEMA
27-
28-
IMPORT_SCHEMA = {
29-
Platform.SENSOR: SENSOR_PLATFORM_SCHEMA,
30-
Platform.NOTIFY: NOTIFY_PLATFORM_SCHEMA,
31-
}
32-
33-
CONFIG_SCHEMA = cv.config_entry_only_config_schema(DOMAIN)
3412

3513
PLATFORMS = [Platform.NOTIFY, Platform.SENSOR]
3614

3715

38-
async def async_setup(hass: HomeAssistant, config: ConfigType) -> bool:
39-
"""Set up the file integration."""
40-
41-
hass.data[DOMAIN] = config
42-
if hass.config_entries.async_entries(DOMAIN):
43-
# We skip import in case we already have config entries
44-
return True
45-
# The use of the legacy notify service was deprecated with HA Core 2024.6.0
46-
# and will be removed with HA Core 2024.12
47-
migrate_notify_issue(hass, DOMAIN, "File++", "2024.12.0")
48-
# The YAML config was imported with HA Core 2024.6.0 and will be removed with
49-
# HA Core 2024.12
50-
ir.async_create_issue(
51-
hass,
52-
HOMEASSISTANT_DOMAIN,
53-
f"deprecated_yaml_{DOMAIN}",
54-
breaks_in_ha_version="2024.12.0",
55-
is_fixable=False,
56-
issue_domain=DOMAIN,
57-
learn_more_url="https://www.home-assistant.io/integrations/file/",
58-
severity=ir.IssueSeverity.WARNING,
59-
translation_key="deprecated_yaml",
60-
translation_placeholders={
61-
"domain": DOMAIN,
62-
"integration_title": "File++",
63-
},
64-
)
65-
66-
# Import the YAML config into separate config entries
67-
platforms_config: dict[Platform, list[ConfigType]] = {
68-
domain: config[domain] for domain in PLATFORMS if domain in config
69-
}
70-
for domain, items in platforms_config.items():
71-
for item in items:
72-
if item[CONF_PLATFORM] == DOMAIN:
73-
file_config_item = IMPORT_SCHEMA[domain](item)
74-
file_config_item[CONF_PLATFORM] = domain
75-
if CONF_SCAN_INTERVAL in file_config_item:
76-
del file_config_item[CONF_SCAN_INTERVAL]
77-
hass.async_create_task(
78-
hass.config_entries.flow.async_init(
79-
DOMAIN,
80-
context={"source": SOURCE_IMPORT},
81-
data=file_config_item,
82-
)
83-
)
84-
85-
return True
86-
87-
8816
async def async_setup_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool:
8917
"""Set up a file component entry."""
9018
config = {**entry.data, **entry.options}
@@ -102,20 +30,6 @@ async def async_setup_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool:
10230
entry, [Platform(entry.data[CONF_PLATFORM])]
10331
)
10432
entry.async_on_unload(entry.add_update_listener(update_listener))
105-
if entry.data[CONF_PLATFORM] == Platform.NOTIFY and CONF_NAME in entry.data:
106-
# New notify entities are being setup through the config entry,
107-
# but during the deprecation period we want to keep the legacy notify platform,
108-
# so we forward the setup config through discovery.
109-
# Only the entities from yaml will still be available as legacy service.
110-
hass.async_create_task(
111-
discovery.async_load_platform(
112-
hass,
113-
Platform.NOTIFY,
114-
DOMAIN,
115-
config,
116-
hass.data[DOMAIN],
117-
)
118-
)
11933

12034
return True
12135

@@ -150,4 +64,4 @@ async def async_migrate_entry(hass: HomeAssistant, config_entry: ConfigEntry) ->
15064
hass.config_entries.async_update_entry(
15165
config_entry, version=2, data=data, options=options
15266
)
153-
return True
67+
return True

custom_components/file_plusplus/config_flow.py

Lines changed: 13 additions & 36 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,8 @@
11
"""Config flow for file++ integration."""
22

3+
from __future__ import annotations
4+
35
from copy import deepcopy
4-
import logging
5-
import os
66
from typing import Any
77

88
import voluptuous as vol
@@ -12,11 +12,9 @@
1212
ConfigFlow,
1313
ConfigFlowResult,
1414
OptionsFlow,
15-
OptionsFlowWithConfigEntry,
1615
)
1716
from homeassistant.const import (
1817
CONF_FILE_PATH,
19-
CONF_FILENAME,
2018
CONF_NAME,
2119
CONF_PLATFORM,
2220
CONF_UNIT_OF_MEASUREMENT,
@@ -36,8 +34,6 @@
3634

3735
from .const import CONF_TIMESTAMP, DEFAULT_NAME, DOMAIN
3836

39-
_LOGGER = logging.getLogger(__name__)
40-
4137
BOOLEAN_SELECTOR = BooleanSelector(BooleanSelectorConfig())
4238
TEMPLATE_SELECTOR = TemplateSelector(TemplateSelectorConfig())
4339
TEXT_SELECTOR = TextSelector(TextSelectorConfig(type=TextSelectorType.TEXT))
@@ -77,9 +73,11 @@ class FileConfigFlowHandler(ConfigFlow, domain=DOMAIN):
7773

7874
@staticmethod
7975
@callback
80-
def async_get_options_flow(config_entry: ConfigEntry) -> OptionsFlow:
76+
def async_get_options_flow(
77+
config_entry: ConfigEntry,
78+
) -> FileOptionsFlowHandler:
8179
"""Get the options flow for this handler."""
82-
return FileOptionsFlowHandler(config_entry)
80+
return FileOptionsFlowHandler()
8381

8482
async def validate_file_path(self, file_path: str) -> bool:
8583
"""Ensure the file path is valid."""
@@ -99,15 +97,15 @@ async def async_step_user(
9997
async def _async_handle_step(
10098
self, platform: str, user_input: dict[str, Any] | None = None
10199
) -> ConfigFlowResult:
102-
"""Handle file config flow step."""
100+
"""Handle file++ config flow step."""
103101
errors: dict[str, str] = {}
104102
if user_input:
105103
user_input[CONF_PLATFORM] = platform
106104
self._async_abort_entries_match(user_input)
107105
if not await self.validate_file_path(user_input[CONF_FILE_PATH]):
108106
errors[CONF_FILE_PATH] = "not_allowed"
109107
else:
110-
title = f"{platform.capitalize()} [{user_input[CONF_FILE_PATH]}]"
108+
title = f"{DEFAULT_NAME} [{user_input[CONF_FILE_PATH]}]"
111109
data = deepcopy(user_input)
112110
options = {}
113111
for key, value in user_input.items():
@@ -123,38 +121,17 @@ async def _async_handle_step(
123121
async def async_step_notify(
124122
self, user_input: dict[str, Any] | None = None
125123
) -> ConfigFlowResult:
126-
"""Handle file notifier config flow."""
124+
"""Handle file++ notifier config flow."""
127125
return await self._async_handle_step(Platform.NOTIFY.value, user_input)
128126

129127
async def async_step_sensor(
130128
self, user_input: dict[str, Any] | None = None
131129
) -> ConfigFlowResult:
132-
"""Handle file sensor config flow."""
130+
"""Handle file++ sensor config flow."""
133131
return await self._async_handle_step(Platform.SENSOR.value, user_input)
134132

135-
async def async_step_import(self, import_data: dict[str, Any]) -> ConfigFlowResult:
136-
"""Import `file`` config from configuration.yaml."""
137-
self._async_abort_entries_match(import_data)
138-
platform = import_data[CONF_PLATFORM]
139-
name: str = import_data.get(CONF_NAME, DEFAULT_NAME)
140-
file_name: str
141-
if platform == Platform.NOTIFY:
142-
file_name = import_data.pop(CONF_FILENAME)
143-
file_path: str = os.path.join(self.hass.config.config_dir, file_name)
144-
import_data[CONF_FILE_PATH] = file_path
145-
else:
146-
file_path = import_data[CONF_FILE_PATH]
147-
title = f"{name} [{file_path}]"
148-
data = deepcopy(import_data)
149-
options = {}
150-
for key, value in import_data.items():
151-
if key not in (CONF_FILE_PATH, CONF_PLATFORM, CONF_NAME):
152-
data.pop(key)
153-
options[key] = value
154-
return self.async_create_entry(title=title, data=data, options=options)
155-
156-
157-
class FileOptionsFlowHandler(OptionsFlowWithConfigEntry):
133+
134+
class FileOptionsFlowHandler(OptionsFlow):
158135
"""Handle File++ options."""
159136

160137
async def async_step_init(
@@ -170,4 +147,4 @@ async def async_step_init(
170147
data_schema=self.add_suggested_values_to_schema(
171148
FILE_OPTIONS_SCHEMAS[platform], self.config_entry.options or {}
172149
),
173-
)
150+
)

custom_components/file_plusplus/const.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -4,5 +4,5 @@
44

55
CONF_TIMESTAMP = "timestamp"
66

7-
DEFAULT_NAME = "file_plusplus"
8-
FILE_ICON = "mdi:file"
7+
DEFAULT_NAME = "file_plusplus" # File++
8+
FILE_ICON = "mdi:file"
Lines changed: 7 additions & 81 deletions
Original file line numberDiff line numberDiff line change
@@ -1,98 +1,24 @@
1-
"""Support for file++ notification."""
1+
"""Support for File++ notification."""
22

33
from __future__ import annotations
44

5-
from functools import partial
6-
import logging
75
import os
86
from typing import Any, TextIO
97

10-
import voluptuous as vol
11-
128
from homeassistant.components.notify import (
13-
ATTR_TITLE,
149
ATTR_TITLE_DEFAULT,
15-
PLATFORM_SCHEMA as NOTIFY_PLATFORM_SCHEMA,
16-
BaseNotificationService,
1710
NotifyEntity,
1811
NotifyEntityFeature,
19-
migrate_notify_issue,
2012
)
2113
from homeassistant.config_entries import ConfigEntry
22-
from homeassistant.const import CONF_FILE_PATH, CONF_FILENAME, CONF_NAME
14+
from homeassistant.const import CONF_FILE_PATH, CONF_NAME
2315
from homeassistant.core import HomeAssistant
2416
from homeassistant.exceptions import ServiceValidationError
25-
import homeassistant.helpers.config_validation as cv
2617
from homeassistant.helpers.entity_platform import AddEntitiesCallback
27-
from homeassistant.helpers.typing import ConfigType, DiscoveryInfoType
2818
import homeassistant.util.dt as dt_util
2919

3020
from .const import CONF_TIMESTAMP, DEFAULT_NAME, DOMAIN, FILE_ICON
3121

32-
_LOGGER = logging.getLogger(__name__)
33-
34-
# The legacy platform schema uses a filename, after import
35-
# The full file path is stored in the config entry
36-
PLATFORM_SCHEMA = NOTIFY_PLATFORM_SCHEMA.extend(
37-
{
38-
vol.Required(CONF_FILENAME): cv.string,
39-
vol.Optional(CONF_TIMESTAMP, default=False): cv.boolean,
40-
}
41-
)
42-
43-
44-
async def async_get_service(
45-
hass: HomeAssistant,
46-
config: ConfigType,
47-
discovery_info: DiscoveryInfoType | None = None,
48-
) -> FileNotificationService | None:
49-
"""Get the file notification service."""
50-
if discovery_info is None:
51-
# We only set up through discovery
52-
return None
53-
file_path: str = discovery_info[CONF_FILE_PATH]
54-
timestamp: bool = discovery_info[CONF_TIMESTAMP]
55-
56-
return FileNotificationService(file_path, timestamp)
57-
58-
59-
class FileNotificationService(BaseNotificationService):
60-
"""Implement the notification service for the File++ service."""
61-
62-
def __init__(self, file_path: str, add_timestamp: bool) -> None:
63-
"""Initialize the service."""
64-
self._file_path = file_path
65-
self.add_timestamp = add_timestamp
66-
67-
async def async_send_message(self, message: str = "", **kwargs: Any) -> None:
68-
"""Send a message to a file."""
69-
# The use of the legacy notify service was deprecated with HA Core 2024.6.0
70-
# and will be removed with HA Core 2024.12
71-
migrate_notify_issue(
72-
self.hass, DOMAIN, "File", "2024.12.0", service_name=self._service_name
73-
)
74-
await self.hass.async_add_executor_job(
75-
partial(self.send_message, message, **kwargs)
76-
)
77-
78-
def send_message(self, message: str = "", **kwargs: Any) -> None:
79-
"""Send a message to a file."""
80-
file: TextIO
81-
filepath = self._file_path
82-
try:
83-
with open(filepath, "a", encoding="utf8") as file:
84-
if self.add_timestamp:
85-
text = f"{dt_util.utcnow().isoformat()} {message}\n"
86-
else:
87-
text = f"{message}\n"
88-
file.write(text)
89-
except OSError as exc:
90-
raise ServiceValidationError(
91-
translation_domain=DOMAIN,
92-
translation_key="write_access_failed",
93-
translation_placeholders={"filename": filepath, "exc": f"{exc!r}"},
94-
) from exc
95-
9622

9723
async def async_setup_entry(
9824
hass: HomeAssistant,
@@ -105,7 +31,7 @@ async def async_setup_entry(
10531

10632

10733
class FileNotifyEntity(NotifyEntity):
108-
"""Implement the notification entity platform for the File service."""
34+
"""Implement the notification entity platform for the File++ service."""
10935

11036
_attr_icon = FILE_ICON
11137
_attr_supported_features = NotifyEntityFeature.TITLE
@@ -115,16 +41,16 @@ def __init__(self, unique_id: str, config: dict[str, Any]) -> None:
11541
self._file_path: str = config[CONF_FILE_PATH]
11642
self._add_timestamp: bool = config.get(CONF_TIMESTAMP, False)
11743
# Only import a name from an imported entity
118-
self._attr_name = config.get(CONF_NAME, DEFAULT_NAME) + "_" + self._file_path.split("/")[-1].replace(".", "_")
44+
self._attr_name = config.get(CONF_NAME, DEFAULT_NAME)
11945
self._attr_unique_id = unique_id
12046

12147
def send_message(self, message: str, title: str | None = None) -> None:
12248
"""Send a message to a file."""
12349
file: TextIO
12450
filepath = self._file_path
125-
message = message.strip()
12651
try:
127-
with open(filepath, "w", encoding="utf8") as file:
52+
with open(filepath, "a", encoding="utf8") as file:
53+
# File++ - Delete header
12854
if self._add_timestamp:
12955
text = f"{dt_util.utcnow().isoformat()} {message}\n"
13056
else:
@@ -135,4 +61,4 @@ def send_message(self, message: str, title: str | None = None) -> None:
13561
translation_domain=DOMAIN,
13662
translation_key="write_access_failed",
13763
translation_placeholders={"filename": filepath, "exc": f"{exc!r}"},
138-
) from exc
64+
) from exc

0 commit comments

Comments
 (0)