-
Notifications
You must be signed in to change notification settings - Fork 3
Expand file tree
/
Copy path__init__.py
More file actions
201 lines (175 loc) · 7.38 KB
/
__init__.py
File metadata and controls
201 lines (175 loc) · 7.38 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
"""Humidity Intelligence integration for Home Assistant."""
from __future__ import annotations
import logging
from homeassistant.config_entries import ConfigEntry
from homeassistant.core import HomeAssistant
from homeassistant.helpers import config_validation as cv
from .const import DOMAIN
from .services import async_register_services, async_unregister_services
from .helpers.cleanup import list_generated_files, remove_files, remove_dashboard
from .ui.register import async_register_cards, async_build_entity_mapping
from .automations import async_setup_entry as async_setup_automations
from .automations import async_unload_entry as async_unload_automations
_LOGGER = logging.getLogger(__name__)
CONFIG_SCHEMA = cv.config_entry_only_config_schema(DOMAIN)
async def async_setup(hass: HomeAssistant, config: dict) -> bool:
"""Set up the Humidity Intelligence integration via YAML."""
if DOMAIN in config:
_LOGGER.warning(
"Configuration via YAML is deprecated for %s; please use the configuration UI.",
DOMAIN,
)
return True
async def async_setup_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool:
"""Set up Humidity Intelligence from a config entry."""
entry.async_on_unload(entry.add_update_listener(_async_options_updated))
effective_config = _effective_entry_config(entry)
hass.data.setdefault(DOMAIN, {})
hass.data[DOMAIN][entry.entry_id] = {
"config": effective_config,
"options": entry.options,
}
await async_register_services(hass)
await hass.config_entries.async_forward_entry_setups(entry, ["sensor", "binary_sensor", "switch"])
await async_setup_automations(hass, entry)
# Prepare UI card YAML for this entry using entity mapping.
try:
mapping = await async_build_entity_mapping(hass, entry.entry_id)
cards = await async_register_cards(hass, entry.entry_id, mapping=mapping)
except Exception:
_LOGGER.exception("Failed to build UI mapping/cards for entry %s", entry.entry_id)
mapping = {}
cards = {}
hass.data[DOMAIN][entry.entry_id]["cards"] = cards
hass.data[DOMAIN][entry.entry_id]["entity_map"] = mapping
hass.async_create_task(_async_refresh_and_dump_cards(hass, entry.entry_id))
ui_layouts = entry.data.get("ui_layouts") or []
if ui_layouts and not entry.data.get("ui_install_done"):
await hass.services.async_call(
DOMAIN,
"dump_cards",
{"entry_id": entry.entry_id},
blocking=False,
)
dashboard_id = "humidity-intelligence"
if "create_dashboard" in ui_layouts:
await hass.services.async_call(
DOMAIN,
"create_dashboard",
{
"entry_id": entry.entry_id,
"layout": "v2_mobile" if "v2_mobile" in ui_layouts else "v2_tablet",
"title": "Humidity Intelligence",
"url_path": dashboard_id,
},
blocking=False,
)
await hass.services.async_call(
"persistent_notification",
"create",
{
"title": "Humidity Intelligence UI Cards",
"message": (
"Cards written to /config/humidity_intelligence_cards_<layout>.yaml. "
"Open File Editor, copy the YAML for your chosen layout(s), and paste into a Manual card."
),
},
blocking=False,
)
data = dict(entry.data)
data["ui_install_done"] = True
if "create_dashboard" in ui_layouts:
data["ui_dashboard_id"] = dashboard_id
hass.config_entries.async_update_entry(entry, data=data)
_LOGGER.info("Humidity Intelligence v2 entry %s set up", entry.entry_id)
return True
async def async_unload_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool:
"""Unload a config entry."""
await hass.config_entries.async_unload_platforms(entry, ["sensor", "binary_sensor", "switch"])
await async_unload_automations(hass, entry)
data = hass.data.get(DOMAIN, {}).get(entry.entry_id, {})
if unsub := data.get("core_unsub"):
unsub()
if unsub := data.get("slope_unsub"):
unsub()
hass.data.get(DOMAIN, {}).pop(entry.entry_id, None)
await async_unregister_services(hass)
return True
async def async_remove_entry(hass: HomeAssistant, entry: ConfigEntry) -> None:
"""Remove config entry data and generated files."""
files = list_generated_files(entry)
dashboard_id = entry.data.get("ui_dashboard_id")
message_lines = [f"/config/{f}" for f in files]
if dashboard_id:
message_lines.append(f"Dashboard: {dashboard_id}")
await hass.services.async_call(
"persistent_notification",
"create",
{
"title": "Humidity Intelligence Cleanup",
"message": "Removing generated files:\n" + "\n".join(message_lines),
},
blocking=False,
)
await hass.async_add_executor_job(remove_files, hass, files)
await remove_dashboard(hass, dashboard_id)
async def _async_refresh_and_dump_cards(hass: HomeAssistant, entry_id: str) -> None:
"""Rebuild mapping and rewrite card files after startup."""
try:
await hass.services.async_call(
DOMAIN,
"refresh_ui",
{"entry_id": entry_id},
blocking=True,
)
await hass.services.async_call(
DOMAIN,
"dump_cards",
{"entry_id": entry_id},
blocking=True,
)
except Exception:
_LOGGER.exception(
"Failed startup refresh/dump for Humidity Intelligence entry %s",
entry_id,
)
def _effective_entry_config(entry: ConfigEntry) -> dict:
config = dict(entry.data or {})
options = dict(entry.options or {})
config.update(options)
return config
async def _async_options_updated(hass: HomeAssistant, entry: ConfigEntry) -> None:
"""Reload entry when options change so runtime lanes immediately honor updates."""
previous_cfg = (
hass.data.get(DOMAIN, {}).get(entry.entry_id, {}).get("config", {}) or {}
)
prev_alert_only = bool(previous_cfg.get("alert_only_mode", _entry_alert_only_mode(entry)))
next_alert_only = _entry_alert_only_mode(entry)
await hass.config_entries.async_reload(entry.entry_id)
if prev_alert_only == next_alert_only:
return
_LOGGER.info(
"HI entry %s alert-only mode changed to %s; regenerating UI card exports.",
entry.entry_id,
next_alert_only,
)
await _async_refresh_and_dump_cards(hass, entry.entry_id)
await hass.services.async_call(
"persistent_notification",
"create",
{
"title": "Humidity Intelligence UI Updated",
"message": (
"Alert-only mode changed. Updated card files were written to "
"/config/humidity_intelligence_cards_<layout>.yaml. "
"Re-copy/paste the layout YAML into your Manual card to apply control visibility changes."
),
},
blocking=False,
)
def _entry_alert_only_mode(entry: ConfigEntry) -> bool:
options = getattr(entry, "options", None) or {}
if "alert_only_mode" in options:
return bool(options.get("alert_only_mode"))
data = getattr(entry, "data", None) or {}
return bool(data.get("alert_only_mode", False))