HomeAssistant-API 5.0.0__tar.gz → 5.0.1__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- {homeassistant_api-5.0.0 → homeassistant_api-5.0.1}/PKG-INFO +1 -1
- {homeassistant_api-5.0.0 → homeassistant_api-5.0.1}/homeassistant_api/errors.py +19 -0
- {homeassistant_api-5.0.0 → homeassistant_api-5.0.1}/homeassistant_api/models/base.py +7 -0
- homeassistant_api-5.0.1/homeassistant_api/models/domains.py +654 -0
- {homeassistant_api-5.0.0 → homeassistant_api-5.0.1}/homeassistant_api/models/events.py +4 -2
- {homeassistant_api-5.0.0 → homeassistant_api-5.0.1}/homeassistant_api/models/states.py +19 -8
- {homeassistant_api-5.0.0 → homeassistant_api-5.0.1}/homeassistant_api/models/websocket.py +10 -5
- {homeassistant_api-5.0.0 → homeassistant_api-5.0.1}/homeassistant_api/processing.py +14 -10
- {homeassistant_api-5.0.0 → homeassistant_api-5.0.1}/homeassistant_api/rawasyncclient.py +20 -15
- {homeassistant_api-5.0.0 → homeassistant_api-5.0.1}/homeassistant_api/rawbaseclient.py +46 -17
- {homeassistant_api-5.0.0 → homeassistant_api-5.0.1}/homeassistant_api/rawclient.py +40 -29
- {homeassistant_api-5.0.0 → homeassistant_api-5.0.1}/homeassistant_api/rawwebsocket.py +20 -11
- {homeassistant_api-5.0.0 → homeassistant_api-5.0.1}/homeassistant_api/utils.py +11 -1
- {homeassistant_api-5.0.0 → homeassistant_api-5.0.1}/homeassistant_api/websocket.py +30 -28
- {homeassistant_api-5.0.0 → homeassistant_api-5.0.1}/pyproject.toml +1 -1
- homeassistant_api-5.0.0/homeassistant_api/models/domains.py +0 -177
- {homeassistant_api-5.0.0 → homeassistant_api-5.0.1}/LICENSE +0 -0
- {homeassistant_api-5.0.0 → homeassistant_api-5.0.1}/README.md +0 -0
- {homeassistant_api-5.0.0 → homeassistant_api-5.0.1}/homeassistant_api/__init__.py +0 -0
- {homeassistant_api-5.0.0 → homeassistant_api-5.0.1}/homeassistant_api/client.py +0 -0
- {homeassistant_api-5.0.0 → homeassistant_api-5.0.1}/homeassistant_api/models/__init__.py +0 -0
- {homeassistant_api-5.0.0 → homeassistant_api-5.0.1}/homeassistant_api/models/entity.py +0 -0
- {homeassistant_api-5.0.0 → homeassistant_api-5.0.1}/homeassistant_api/models/history.py +0 -0
- {homeassistant_api-5.0.0 → homeassistant_api-5.0.1}/homeassistant_api/models/logbook.py +0 -0
- {homeassistant_api-5.0.0 → homeassistant_api-5.0.1}/homeassistant_api/py.typed +0 -0
|
@@ -10,10 +10,29 @@ class HomeassistantAPIError(Exception):
|
|
|
10
10
|
class RequestError(HomeassistantAPIError):
|
|
11
11
|
"""Error raised when an issue occurs when requesting to Homeassistant."""
|
|
12
12
|
|
|
13
|
+
def __init__(
|
|
14
|
+
self, data: Optional[str], /, url: str, message: Optional[str] = None
|
|
15
|
+
) -> None:
|
|
16
|
+
if message is not None:
|
|
17
|
+
super().__init__(
|
|
18
|
+
message
|
|
19
|
+
+ f" {url!r}"
|
|
20
|
+
+ (f" with data: {data!r}" if data is not None else "")
|
|
21
|
+
)
|
|
22
|
+
elif data is None:
|
|
23
|
+
super().__init__(f"An error occurred while making the request to {url!r}")
|
|
24
|
+
else:
|
|
25
|
+
super().__init__(
|
|
26
|
+
f"An error occurred while making the request to {url!r} with data: {data!r}"
|
|
27
|
+
)
|
|
28
|
+
|
|
13
29
|
|
|
14
30
|
class RequestTimeoutError(RequestError):
|
|
15
31
|
"""Error raised when a request times out."""
|
|
16
32
|
|
|
33
|
+
def __init__(self, message: str, url: str) -> None:
|
|
34
|
+
super().__init__(None, url, message)
|
|
35
|
+
|
|
17
36
|
|
|
18
37
|
class ResponseError(HomeassistantAPIError):
|
|
19
38
|
"""Error raised when an issue occurs in a response from Homeassistant."""
|
|
@@ -6,6 +6,11 @@ from typing import Annotated
|
|
|
6
6
|
from pydantic import BaseModel as PydanticBaseModel
|
|
7
7
|
from pydantic import ConfigDict, PlainSerializer
|
|
8
8
|
|
|
9
|
+
__all__ = (
|
|
10
|
+
"BaseModel",
|
|
11
|
+
"DatetimeIsoField",
|
|
12
|
+
)
|
|
13
|
+
|
|
9
14
|
DatetimeIsoField = Annotated[
|
|
10
15
|
datetime,
|
|
11
16
|
PlainSerializer(lambda x: x.isoformat(), return_type=str, when_used="json"),
|
|
@@ -18,4 +23,6 @@ class BaseModel(PydanticBaseModel):
|
|
|
18
23
|
model_config = ConfigDict(
|
|
19
24
|
arbitrary_types_allowed=True,
|
|
20
25
|
validate_assignment=True,
|
|
26
|
+
extra="forbid",
|
|
27
|
+
protected_namespaces=(),
|
|
21
28
|
)
|
|
@@ -0,0 +1,654 @@
|
|
|
1
|
+
"""File for Service and Domain data models"""
|
|
2
|
+
|
|
3
|
+
from __future__ import annotations
|
|
4
|
+
|
|
5
|
+
import gc
|
|
6
|
+
import inspect
|
|
7
|
+
from enum import Enum
|
|
8
|
+
from typing import (
|
|
9
|
+
TYPE_CHECKING,
|
|
10
|
+
Any,
|
|
11
|
+
Coroutine,
|
|
12
|
+
Dict,
|
|
13
|
+
List,
|
|
14
|
+
Optional,
|
|
15
|
+
Tuple,
|
|
16
|
+
Union,
|
|
17
|
+
cast,
|
|
18
|
+
)
|
|
19
|
+
|
|
20
|
+
from pydantic import Field
|
|
21
|
+
|
|
22
|
+
from homeassistant_api.errors import RequestError
|
|
23
|
+
from homeassistant_api.utils import JSONType
|
|
24
|
+
|
|
25
|
+
from .base import BaseModel
|
|
26
|
+
from .states import State
|
|
27
|
+
|
|
28
|
+
if TYPE_CHECKING:
|
|
29
|
+
from homeassistant_api import Client, WebsocketClient
|
|
30
|
+
|
|
31
|
+
|
|
32
|
+
class Domain(BaseModel):
|
|
33
|
+
"""Model representing the domain that services belong to."""
|
|
34
|
+
|
|
35
|
+
def __init__(
|
|
36
|
+
self,
|
|
37
|
+
*args,
|
|
38
|
+
_client: Optional[Union["Client", "WebsocketClient"]] = None,
|
|
39
|
+
**kwargs,
|
|
40
|
+
) -> None:
|
|
41
|
+
super().__init__(*args, **kwargs)
|
|
42
|
+
if _client is None:
|
|
43
|
+
raise ValueError("No client passed.")
|
|
44
|
+
object.__setattr__(self, "_client", _client)
|
|
45
|
+
|
|
46
|
+
_client: Union["Client", "WebsocketClient"]
|
|
47
|
+
domain_id: str = Field(
|
|
48
|
+
...,
|
|
49
|
+
description="The name of the domain that services belong to. "
|
|
50
|
+
"(e.g. :code:`frontend` in :code:`frontend.reload_themes`",
|
|
51
|
+
)
|
|
52
|
+
services: Dict[str, "Service"] = Field(
|
|
53
|
+
{},
|
|
54
|
+
description="A dictionary of all services belonging to the domain indexed by their names",
|
|
55
|
+
)
|
|
56
|
+
|
|
57
|
+
@classmethod
|
|
58
|
+
def from_json(
|
|
59
|
+
cls, json: Dict[str, JSONType], client: Union["Client", "WebsocketClient"]
|
|
60
|
+
) -> "Domain":
|
|
61
|
+
"""Constructs Domain and Service models from json data."""
|
|
62
|
+
if "domain" not in json or "services" not in json:
|
|
63
|
+
raise ValueError("Missing services or domain attribute in json argument.")
|
|
64
|
+
domain = cls(domain_id=cast(str, json.get("domain")), _client=client)
|
|
65
|
+
services = cast(dict[str, dict[str, JSONType]], json.get("services"))
|
|
66
|
+
assert isinstance(services, dict)
|
|
67
|
+
for service_id, data in services.items():
|
|
68
|
+
domain._add_service(service_id, **data)
|
|
69
|
+
return domain
|
|
70
|
+
|
|
71
|
+
def _add_service(self, service_id: str, **data) -> None:
|
|
72
|
+
"""Registers services into a domain to be used or accessed. Used internally."""
|
|
73
|
+
# raise ValueError(data)
|
|
74
|
+
self.services.update(
|
|
75
|
+
{
|
|
76
|
+
service_id: Service(
|
|
77
|
+
service_id=service_id,
|
|
78
|
+
domain=self,
|
|
79
|
+
**data,
|
|
80
|
+
)
|
|
81
|
+
}
|
|
82
|
+
)
|
|
83
|
+
|
|
84
|
+
def get_service(self, service_id: str) -> Optional["Service"]:
|
|
85
|
+
"""Return a Service with the given service_id, returns None if no such service exists"""
|
|
86
|
+
return self.services.get(service_id)
|
|
87
|
+
|
|
88
|
+
def __getattr__(self, attr: str):
|
|
89
|
+
"""Allows services accessible as attributes"""
|
|
90
|
+
if attr in self.services:
|
|
91
|
+
return self.get_service(attr)
|
|
92
|
+
try:
|
|
93
|
+
return super().__getattribute__(attr)
|
|
94
|
+
except AttributeError as err:
|
|
95
|
+
try:
|
|
96
|
+
return object.__getattribute__(self, attr)
|
|
97
|
+
except AttributeError as e:
|
|
98
|
+
raise e from err
|
|
99
|
+
|
|
100
|
+
|
|
101
|
+
# Sources:
|
|
102
|
+
# https://developers.home-assistant.io/docs/dev_101_services/
|
|
103
|
+
# https://www.home-assistant.io/docs/blueprint/selectors/#date-selector
|
|
104
|
+
# https://github.com/home-assistant/frontend/blob/dev/src/data/selector.ts
|
|
105
|
+
# https://github.com/home-assistant/home-assistant-js-websocket/blob/master/lib/types.ts
|
|
106
|
+
|
|
107
|
+
|
|
108
|
+
# Helpers
|
|
109
|
+
class ServiceFieldSelectorEntityFilter(BaseModel):
|
|
110
|
+
integration: Optional[str] = None
|
|
111
|
+
domain: Optional[Union[List[str], str]] = None
|
|
112
|
+
device_class: Optional[Union[List[str], str]] = None
|
|
113
|
+
supported_features: Optional[Union[List[int], int]] = None
|
|
114
|
+
|
|
115
|
+
|
|
116
|
+
class ServiceFieldSelectorDeviceFilter(BaseModel):
|
|
117
|
+
integration: Optional[str] = None
|
|
118
|
+
manufacturer: Optional[str] = None
|
|
119
|
+
model: Optional[str] = None
|
|
120
|
+
model_id: Optional[str] = None
|
|
121
|
+
|
|
122
|
+
|
|
123
|
+
class CropOptions(BaseModel):
|
|
124
|
+
round: bool
|
|
125
|
+
type: Optional[str] = None # "image/jpeg" / "image/png"
|
|
126
|
+
quality: Optional[Union[int, float]] = None
|
|
127
|
+
aspectRatio: Optional[Union[int, float]] = None
|
|
128
|
+
|
|
129
|
+
|
|
130
|
+
class SelectBoxOptionImage(BaseModel):
|
|
131
|
+
src: str
|
|
132
|
+
src_dark: Optional[str] = None
|
|
133
|
+
flip_rtl: Optional[bool] = None
|
|
134
|
+
|
|
135
|
+
|
|
136
|
+
class ServiceFieldSelectorNumberMode(str, Enum):
|
|
137
|
+
BOX = "box"
|
|
138
|
+
SLIDER = "slider"
|
|
139
|
+
|
|
140
|
+
|
|
141
|
+
class ServiceFieldSelectorSelectMode(str, Enum):
|
|
142
|
+
LIST = "list"
|
|
143
|
+
DROPDOWN = "dropdown"
|
|
144
|
+
BOX = "box"
|
|
145
|
+
|
|
146
|
+
|
|
147
|
+
class ServiceFieldSelectorQRCodeErrorCorrectionLevel(str, Enum):
|
|
148
|
+
LOW = "low"
|
|
149
|
+
MEDIUM = "medium"
|
|
150
|
+
QUARTILE = "quartile"
|
|
151
|
+
HIGH = "high"
|
|
152
|
+
|
|
153
|
+
|
|
154
|
+
class ServiceFieldSelectorTextType(str, Enum):
|
|
155
|
+
NUMBER = "number"
|
|
156
|
+
TEXT = "text"
|
|
157
|
+
SEARCH = "search"
|
|
158
|
+
TEL = "tel"
|
|
159
|
+
URL = "url"
|
|
160
|
+
EMAIL = "email"
|
|
161
|
+
PASSWORD = "password"
|
|
162
|
+
DATE = "date"
|
|
163
|
+
MONTH = "month"
|
|
164
|
+
WEEK = "week"
|
|
165
|
+
TIME = "time"
|
|
166
|
+
DATETIME_LOCAL = "datetime-local"
|
|
167
|
+
COLOR = "color"
|
|
168
|
+
|
|
169
|
+
|
|
170
|
+
# Selectors
|
|
171
|
+
class ServiceFieldSelectorAction(BaseModel):
|
|
172
|
+
optionsInSidebar: Optional[bool] = None
|
|
173
|
+
|
|
174
|
+
|
|
175
|
+
class ServiceFieldSelectorAddon(BaseModel):
|
|
176
|
+
name: Optional[str] = None
|
|
177
|
+
slug: Optional[str] = None
|
|
178
|
+
|
|
179
|
+
|
|
180
|
+
class ServiceFieldSelectorArea(BaseModel):
|
|
181
|
+
entity: Optional[
|
|
182
|
+
Union[List[ServiceFieldSelectorEntityFilter], ServiceFieldSelectorEntityFilter]
|
|
183
|
+
] = None
|
|
184
|
+
device: Optional[
|
|
185
|
+
Union[List[ServiceFieldSelectorDeviceFilter], ServiceFieldSelectorDeviceFilter]
|
|
186
|
+
] = None
|
|
187
|
+
multiple: Optional[bool] = None
|
|
188
|
+
|
|
189
|
+
|
|
190
|
+
class ServiceFieldSelectorAreasDisplay(BaseModel):
|
|
191
|
+
pass
|
|
192
|
+
|
|
193
|
+
|
|
194
|
+
class ServiceFieldSelectorAttribute(BaseModel):
|
|
195
|
+
entity_id: Optional[Union[List[str], str]] = None
|
|
196
|
+
hide_attributes: Optional[List[str]] = None
|
|
197
|
+
|
|
198
|
+
|
|
199
|
+
class ServiceFieldSelectorAssistPipeline(BaseModel):
|
|
200
|
+
include_last_used: Optional[bool] = None
|
|
201
|
+
|
|
202
|
+
|
|
203
|
+
class ServiceFieldSelectorBackground(BaseModel):
|
|
204
|
+
original: Optional[bool] = None
|
|
205
|
+
crop: Optional[CropOptions] = None
|
|
206
|
+
|
|
207
|
+
|
|
208
|
+
class ServiceFieldSelectorBackupLocation(BaseModel):
|
|
209
|
+
pass
|
|
210
|
+
|
|
211
|
+
|
|
212
|
+
class ServiceFieldSelectorBoolean(BaseModel):
|
|
213
|
+
pass
|
|
214
|
+
|
|
215
|
+
|
|
216
|
+
class ServiceFieldSelectorButtonToggle(BaseModel):
|
|
217
|
+
options: List[Union[str, ServiceFieldSelectorSelectOption]]
|
|
218
|
+
translation_key: Optional[str] = None
|
|
219
|
+
sort: Optional[bool] = None
|
|
220
|
+
|
|
221
|
+
|
|
222
|
+
class ServiceFieldSelectorColorRGB(BaseModel):
|
|
223
|
+
pass
|
|
224
|
+
|
|
225
|
+
|
|
226
|
+
class ServiceFieldSelectorColorTemp(BaseModel):
|
|
227
|
+
unit: Optional[str] = None
|
|
228
|
+
min: Optional[Union[int, float]] = None
|
|
229
|
+
max: Optional[Union[int, float]] = None
|
|
230
|
+
min_mireds: Optional[Union[int, float]] = None
|
|
231
|
+
max_mireds: Optional[Union[int, float]] = None
|
|
232
|
+
|
|
233
|
+
|
|
234
|
+
class ServiceFieldSelectorCondition(BaseModel):
|
|
235
|
+
optionsInSidebar: Optional[bool] = None
|
|
236
|
+
|
|
237
|
+
|
|
238
|
+
class ServiceFieldSelectorConfigEntry(BaseModel):
|
|
239
|
+
integration: Optional[str] = None
|
|
240
|
+
|
|
241
|
+
|
|
242
|
+
class ServiceFieldSelectorConstant(BaseModel):
|
|
243
|
+
label: Optional[str] = None
|
|
244
|
+
value: Union[str, int, float, bool]
|
|
245
|
+
translation_key: Optional[str] = None
|
|
246
|
+
|
|
247
|
+
|
|
248
|
+
class ServiceFieldSelectorConversationAgent(BaseModel):
|
|
249
|
+
language: Optional[str] = None # filtering by language not supported
|
|
250
|
+
|
|
251
|
+
|
|
252
|
+
class ServiceFieldSelectorCountry(BaseModel):
|
|
253
|
+
countries: List[str]
|
|
254
|
+
no_sort: Optional[bool] = None
|
|
255
|
+
|
|
256
|
+
|
|
257
|
+
class ServiceFieldSelectorDate(BaseModel):
|
|
258
|
+
pass
|
|
259
|
+
|
|
260
|
+
|
|
261
|
+
class ServiceFieldSelectorDateTime(BaseModel):
|
|
262
|
+
pass
|
|
263
|
+
|
|
264
|
+
|
|
265
|
+
class ServiceFieldSelectorDevice(BaseModel):
|
|
266
|
+
entity: Optional[
|
|
267
|
+
Union[List[ServiceFieldSelectorEntityFilter], ServiceFieldSelectorEntityFilter]
|
|
268
|
+
] = None
|
|
269
|
+
filter: Optional[
|
|
270
|
+
Union[List[ServiceFieldSelectorDeviceFilter], ServiceFieldSelectorDeviceFilter]
|
|
271
|
+
] = None
|
|
272
|
+
multiple: Optional[bool] = None
|
|
273
|
+
|
|
274
|
+
|
|
275
|
+
class ServiceFieldSelectorDeviceLegacy(ServiceFieldSelectorDevice):
|
|
276
|
+
integration: Optional[str] = None
|
|
277
|
+
manufacturer: Optional[str] = None
|
|
278
|
+
model: Optional[str] = None
|
|
279
|
+
|
|
280
|
+
|
|
281
|
+
class ServiceFieldSelectorDuration(BaseModel):
|
|
282
|
+
enable_day: Optional[bool] = None
|
|
283
|
+
enable_millisecond: Optional[bool] = None
|
|
284
|
+
|
|
285
|
+
|
|
286
|
+
class ServiceFieldSelectorEntity(BaseModel):
|
|
287
|
+
multiple: Optional[bool] = None
|
|
288
|
+
include_entities: Optional[List[str]] = None
|
|
289
|
+
exclude_entities: Optional[List[str]] = None
|
|
290
|
+
filter: Optional[
|
|
291
|
+
Union[List[ServiceFieldSelectorEntityFilter], ServiceFieldSelectorEntityFilter]
|
|
292
|
+
] = None
|
|
293
|
+
reorder: Optional[bool] = None
|
|
294
|
+
|
|
295
|
+
|
|
296
|
+
class ServiceFieldSelectorEntityLegacy(ServiceFieldSelectorEntity):
|
|
297
|
+
integration: Optional[str] = None
|
|
298
|
+
domain: Optional[Union[List[str], str]] = None
|
|
299
|
+
device_class: Optional[Union[List[str], str]] = None
|
|
300
|
+
|
|
301
|
+
|
|
302
|
+
class ServiceFieldSelectorFloor(BaseModel):
|
|
303
|
+
entity: Optional[
|
|
304
|
+
Union[List[ServiceFieldSelectorEntityFilter], ServiceFieldSelectorEntityFilter]
|
|
305
|
+
] = None
|
|
306
|
+
device: Optional[
|
|
307
|
+
Union[List[ServiceFieldSelectorDeviceFilter], ServiceFieldSelectorDeviceFilter]
|
|
308
|
+
] = None
|
|
309
|
+
multiple: Optional[bool] = None
|
|
310
|
+
|
|
311
|
+
|
|
312
|
+
class ServiceFieldSelectorFile(BaseModel):
|
|
313
|
+
accept: str
|
|
314
|
+
|
|
315
|
+
|
|
316
|
+
class ServiceFieldSelectorIcon(BaseModel):
|
|
317
|
+
placeholder: Optional[str] = None
|
|
318
|
+
fallbackPath: Optional[str] = None
|
|
319
|
+
|
|
320
|
+
|
|
321
|
+
class ServiceFieldSelectorImage(BaseModel):
|
|
322
|
+
original: Optional[bool] = None
|
|
323
|
+
crop: Optional[CropOptions] = None
|
|
324
|
+
|
|
325
|
+
|
|
326
|
+
class ServiceFieldSelectorLabel(BaseModel):
|
|
327
|
+
multiple: Optional[bool] = None
|
|
328
|
+
|
|
329
|
+
|
|
330
|
+
class ServiceFieldSelectorLanguage(BaseModel):
|
|
331
|
+
languages: Optional[List[str]] = None
|
|
332
|
+
native_name: Optional[bool] = None
|
|
333
|
+
no_sort: Optional[bool] = None
|
|
334
|
+
|
|
335
|
+
|
|
336
|
+
class ServiceFieldSelectorLocation(BaseModel):
|
|
337
|
+
radius: Optional[bool] = None
|
|
338
|
+
radius_readonly: Optional[bool] = None
|
|
339
|
+
icon: Optional[str] = None
|
|
340
|
+
|
|
341
|
+
|
|
342
|
+
class ServiceFieldSelectorMedia(BaseModel):
|
|
343
|
+
accept: Optional[List[str]] = None
|
|
344
|
+
|
|
345
|
+
|
|
346
|
+
class ServiceFieldSelectorNavigation(BaseModel):
|
|
347
|
+
pass
|
|
348
|
+
|
|
349
|
+
|
|
350
|
+
class ServiceFieldSelectorNumber(BaseModel):
|
|
351
|
+
min: Optional[Union[int, float]] = None
|
|
352
|
+
max: Optional[Union[int, float]] = None
|
|
353
|
+
step: Optional[Union[Union[int, float], str]] = None
|
|
354
|
+
unit_of_measurement: Optional[str] = None
|
|
355
|
+
mode: Optional[ServiceFieldSelectorNumberMode] = None
|
|
356
|
+
slider_ticks: Optional[bool] = None
|
|
357
|
+
translation_key: Optional[str] = None
|
|
358
|
+
|
|
359
|
+
|
|
360
|
+
class ServiceFieldSelectorObjectField(BaseModel):
|
|
361
|
+
selector: ServiceFieldSelector
|
|
362
|
+
label: Optional[str] = None
|
|
363
|
+
required: Optional[bool] = None
|
|
364
|
+
|
|
365
|
+
|
|
366
|
+
class ServiceFieldSelectorObject(BaseModel):
|
|
367
|
+
label_field: Optional[str] = None
|
|
368
|
+
description_field: Optional[str] = None
|
|
369
|
+
translation_key: Optional[str] = None
|
|
370
|
+
fields: Optional[Dict[str, ServiceFieldSelectorObjectField]] = None
|
|
371
|
+
multiple: Optional[bool] = None
|
|
372
|
+
|
|
373
|
+
|
|
374
|
+
class ServiceFieldSelectorQRCode(BaseModel):
|
|
375
|
+
data: str
|
|
376
|
+
scale: Optional[Union[int, float]] = None
|
|
377
|
+
error_correction_level: Optional[ServiceFieldSelectorQRCodeErrorCorrectionLevel] = (
|
|
378
|
+
None
|
|
379
|
+
)
|
|
380
|
+
center_image: Optional[str] = None
|
|
381
|
+
|
|
382
|
+
|
|
383
|
+
class ServiceFieldSelectorSelectOption(BaseModel):
|
|
384
|
+
label: str
|
|
385
|
+
value: Any
|
|
386
|
+
description: Optional[str] = None
|
|
387
|
+
image: Optional[Union[str, SelectBoxOptionImage]] = None
|
|
388
|
+
disable: Optional[bool] = None
|
|
389
|
+
|
|
390
|
+
|
|
391
|
+
class ServiceFieldSelectorSelect(BaseModel):
|
|
392
|
+
multiple: Optional[bool] = None
|
|
393
|
+
custom_value: Optional[bool] = None
|
|
394
|
+
mode: Optional[ServiceFieldSelectorSelectMode] = None
|
|
395
|
+
options: List[Union[str, ServiceFieldSelectorSelectOption]]
|
|
396
|
+
translation_key: Optional[str] = None
|
|
397
|
+
sort: Optional[bool] = None
|
|
398
|
+
reorder: Optional[bool] = None
|
|
399
|
+
box_max_columns: Optional[int] = None
|
|
400
|
+
|
|
401
|
+
|
|
402
|
+
class ServiceFieldSelectorSelector(BaseModel):
|
|
403
|
+
pass
|
|
404
|
+
|
|
405
|
+
|
|
406
|
+
class ServiceFieldSelectorStateOption(BaseModel):
|
|
407
|
+
label: str
|
|
408
|
+
value: Any
|
|
409
|
+
|
|
410
|
+
|
|
411
|
+
class ServiceFieldSelectorState(BaseModel):
|
|
412
|
+
extra_options: Optional[List[ServiceFieldSelectorStateOption]] = None
|
|
413
|
+
entity_id: Optional[Union[str, List[str]]] = None
|
|
414
|
+
attribute: Optional[str] = None
|
|
415
|
+
hide_states: Optional[List[str]] = None
|
|
416
|
+
multiple: Optional[bool] = None
|
|
417
|
+
|
|
418
|
+
|
|
419
|
+
class ServiceFieldSelectorStatistic(BaseModel):
|
|
420
|
+
device_class: Optional[str] = None
|
|
421
|
+
multiple: Optional[bool] = None
|
|
422
|
+
|
|
423
|
+
|
|
424
|
+
class ServiceFieldSelectorTarget(BaseModel):
|
|
425
|
+
entity: Optional[
|
|
426
|
+
Union[List[ServiceFieldSelectorEntityFilter], ServiceFieldSelectorEntityFilter]
|
|
427
|
+
] = None
|
|
428
|
+
device: Optional[
|
|
429
|
+
Union[List[ServiceFieldSelectorDeviceFilter], ServiceFieldSelectorDeviceFilter]
|
|
430
|
+
] = None
|
|
431
|
+
|
|
432
|
+
|
|
433
|
+
class ServiceFieldSelectorTemplate(BaseModel):
|
|
434
|
+
pass
|
|
435
|
+
|
|
436
|
+
|
|
437
|
+
class ServiceFieldSelectorSTT(BaseModel):
|
|
438
|
+
language: Optional[str] = None
|
|
439
|
+
|
|
440
|
+
|
|
441
|
+
class ServiceFieldSelectorText(BaseModel):
|
|
442
|
+
multiline: Optional[bool] = None
|
|
443
|
+
type: Optional[ServiceFieldSelectorTextType] = None
|
|
444
|
+
prefix: Optional[str] = None
|
|
445
|
+
suffix: Optional[str] = None
|
|
446
|
+
autocomplete: Optional[str] = None
|
|
447
|
+
multiple: Optional[bool] = None
|
|
448
|
+
|
|
449
|
+
|
|
450
|
+
class ServiceFieldSelectorTheme(BaseModel):
|
|
451
|
+
include_default: Optional[bool] = None
|
|
452
|
+
|
|
453
|
+
|
|
454
|
+
class ServiceFieldSelectorTime(BaseModel):
|
|
455
|
+
no_second: Optional[bool] = None
|
|
456
|
+
|
|
457
|
+
|
|
458
|
+
class ServiceFieldSelectorTrigger(BaseModel):
|
|
459
|
+
pass
|
|
460
|
+
|
|
461
|
+
|
|
462
|
+
class ServiceFieldSelectorTTS(BaseModel):
|
|
463
|
+
language: Optional[str] = None
|
|
464
|
+
|
|
465
|
+
|
|
466
|
+
class ServiceFieldSelectorTTSVoice(BaseModel):
|
|
467
|
+
engineId: Optional[str] = None
|
|
468
|
+
language: Optional[str] = None
|
|
469
|
+
|
|
470
|
+
|
|
471
|
+
class ServiceFieldSelectorUIAction(BaseModel):
|
|
472
|
+
pass
|
|
473
|
+
|
|
474
|
+
|
|
475
|
+
class ServiceFieldSelectorUIColor(BaseModel):
|
|
476
|
+
default_color: Optional[str] = None
|
|
477
|
+
include_none: Optional[bool] = None
|
|
478
|
+
include_state: Optional[bool] = None
|
|
479
|
+
|
|
480
|
+
|
|
481
|
+
class ServiceFieldSelectorUIStateContext(BaseModel):
|
|
482
|
+
entity_id: Optional[str] = None
|
|
483
|
+
allow_name: Optional[bool] = None
|
|
484
|
+
|
|
485
|
+
|
|
486
|
+
class ServiceFieldSelector(BaseModel):
|
|
487
|
+
action: Optional[ServiceFieldSelectorAction] = None
|
|
488
|
+
addon: Optional[ServiceFieldSelectorAddon] = None
|
|
489
|
+
area: Optional[ServiceFieldSelectorArea] = None
|
|
490
|
+
areas_display: Optional[ServiceFieldSelectorAreasDisplay] = None
|
|
491
|
+
attribute: Optional[ServiceFieldSelectorAttribute] = None
|
|
492
|
+
assist_pipeline: Optional[ServiceFieldSelectorAssistPipeline] = None
|
|
493
|
+
backup_location: Optional[ServiceFieldSelectorBackupLocation] = None
|
|
494
|
+
background: Optional[ServiceFieldSelectorBackground] = None
|
|
495
|
+
boolean: Optional[ServiceFieldSelectorBoolean] = None
|
|
496
|
+
button_toggle: Optional[ServiceFieldSelectorButtonToggle] = None
|
|
497
|
+
color_rgb: Optional[ServiceFieldSelectorColorRGB] = None
|
|
498
|
+
color_temp: Optional[ServiceFieldSelectorColorTemp] = None
|
|
499
|
+
condition: Optional[ServiceFieldSelectorCondition] = None
|
|
500
|
+
config_entry: Optional[ServiceFieldSelectorConfigEntry] = None
|
|
501
|
+
constant: Optional[ServiceFieldSelectorConstant] = None
|
|
502
|
+
conversation_agent: Optional[ServiceFieldSelectorConversationAgent] = None
|
|
503
|
+
country: Optional[ServiceFieldSelectorCountry] = None
|
|
504
|
+
date: Optional[ServiceFieldSelectorDate] = None
|
|
505
|
+
datetime: Optional[ServiceFieldSelectorDateTime] = None
|
|
506
|
+
device: Optional[
|
|
507
|
+
Union[ServiceFieldSelectorDevice, ServiceFieldSelectorDeviceLegacy]
|
|
508
|
+
] = None
|
|
509
|
+
duration: Optional[ServiceFieldSelectorDuration] = None
|
|
510
|
+
entity: Optional[
|
|
511
|
+
Union[ServiceFieldSelectorEntity, ServiceFieldSelectorEntityLegacy]
|
|
512
|
+
] = None
|
|
513
|
+
floor: Optional[ServiceFieldSelectorFloor] = None
|
|
514
|
+
file: Optional[ServiceFieldSelectorFile] = None
|
|
515
|
+
icon: Optional[ServiceFieldSelectorIcon] = None
|
|
516
|
+
image: Optional[ServiceFieldSelectorImage] = None
|
|
517
|
+
label: Optional[ServiceFieldSelectorLabel] = None
|
|
518
|
+
language: Optional[ServiceFieldSelectorLanguage] = None
|
|
519
|
+
location: Optional[ServiceFieldSelectorLocation] = None
|
|
520
|
+
media: Optional[ServiceFieldSelectorMedia] = None
|
|
521
|
+
navigation: Optional[ServiceFieldSelectorNavigation] = None
|
|
522
|
+
number: Optional[ServiceFieldSelectorNumber] = None
|
|
523
|
+
object: Optional[ServiceFieldSelectorObject] = None
|
|
524
|
+
qr_code: Optional[ServiceFieldSelectorQRCode] = None
|
|
525
|
+
select: Optional[ServiceFieldSelectorSelect] = None
|
|
526
|
+
selector: Optional[ServiceFieldSelectorSelector] = None
|
|
527
|
+
state: Optional[ServiceFieldSelectorState] = None
|
|
528
|
+
statistic: Optional[ServiceFieldSelectorStatistic] = None
|
|
529
|
+
target: Optional[ServiceFieldSelectorTarget] = None
|
|
530
|
+
template: Optional[ServiceFieldSelectorTemplate] = None
|
|
531
|
+
stt: Optional[ServiceFieldSelectorSTT] = None
|
|
532
|
+
text: Optional[ServiceFieldSelectorText] = None
|
|
533
|
+
theme: Optional[ServiceFieldSelectorTheme] = None
|
|
534
|
+
time: Optional[ServiceFieldSelectorTime] = None
|
|
535
|
+
trigger: Optional[ServiceFieldSelectorTrigger] = None
|
|
536
|
+
tts: Optional[ServiceFieldSelectorTTS] = None
|
|
537
|
+
tts_voice: Optional[ServiceFieldSelectorTTSVoice] = None
|
|
538
|
+
ui_action: Optional[ServiceFieldSelectorUIAction] = None
|
|
539
|
+
ui_color: Optional[ServiceFieldSelectorUIColor] = None
|
|
540
|
+
ui_state_content: Optional[ServiceFieldSelectorUIStateContext] = None
|
|
541
|
+
|
|
542
|
+
|
|
543
|
+
# Service bases
|
|
544
|
+
|
|
545
|
+
|
|
546
|
+
class ServiceFieldFilter(BaseModel):
|
|
547
|
+
supported_features: Optional[Union[List[int], int]] = (
|
|
548
|
+
None # Bitset (any needs to be supported [or all within specified list])
|
|
549
|
+
)
|
|
550
|
+
attribute: Optional[Dict[str, Union[List[str], str]]] = None
|
|
551
|
+
|
|
552
|
+
|
|
553
|
+
class ServiceField(BaseModel):
|
|
554
|
+
"""Model for service parameters/fields."""
|
|
555
|
+
|
|
556
|
+
description: Optional[str] = None
|
|
557
|
+
example: Optional[JSONType] = None
|
|
558
|
+
default: Optional[JSONType] = None
|
|
559
|
+
name: Optional[str] = None
|
|
560
|
+
required: Optional[bool] = None
|
|
561
|
+
advanced: Optional[bool] = None
|
|
562
|
+
selector: Optional[ServiceFieldSelector] = None
|
|
563
|
+
filter: Optional[ServiceFieldFilter] = None
|
|
564
|
+
|
|
565
|
+
|
|
566
|
+
class ServiceFieldCollection(BaseModel):
|
|
567
|
+
collapsed: Optional[bool] = None
|
|
568
|
+
fields: Dict[str, ServiceField]
|
|
569
|
+
|
|
570
|
+
|
|
571
|
+
class ServiceResponse(BaseModel):
|
|
572
|
+
optional: Optional[bool] = None
|
|
573
|
+
|
|
574
|
+
|
|
575
|
+
class Service(BaseModel):
|
|
576
|
+
"""Model representing services from homeassistant"""
|
|
577
|
+
|
|
578
|
+
service_id: str
|
|
579
|
+
domain: Domain = Field(exclude=True, repr=False)
|
|
580
|
+
name: str
|
|
581
|
+
description: Optional[str] = None
|
|
582
|
+
fields: Optional[Dict[str, Union[ServiceField, ServiceFieldCollection]]] = None
|
|
583
|
+
target: Optional[ServiceFieldSelectorTarget] = None
|
|
584
|
+
response: Optional[ServiceResponse] = None
|
|
585
|
+
|
|
586
|
+
def trigger(self, **service_data) -> Union[
|
|
587
|
+
Tuple[State, ...],
|
|
588
|
+
Tuple[Tuple[State, ...], dict[str, JSONType]],
|
|
589
|
+
dict[str, JSONType],
|
|
590
|
+
None,
|
|
591
|
+
]:
|
|
592
|
+
"""Triggers the service associated with this object."""
|
|
593
|
+
try:
|
|
594
|
+
return self.domain._client.trigger_service_with_response(
|
|
595
|
+
self.domain.domain_id,
|
|
596
|
+
self.service_id,
|
|
597
|
+
**service_data,
|
|
598
|
+
)
|
|
599
|
+
except RequestError:
|
|
600
|
+
return self.domain._client.trigger_service(
|
|
601
|
+
self.domain.domain_id,
|
|
602
|
+
self.service_id,
|
|
603
|
+
**service_data,
|
|
604
|
+
)
|
|
605
|
+
|
|
606
|
+
async def async_trigger(
|
|
607
|
+
self, **service_data
|
|
608
|
+
) -> Union[Tuple[State, ...], Tuple[Tuple[State, ...], dict[str, JSONType]]]:
|
|
609
|
+
"""Triggers the service associated with this object."""
|
|
610
|
+
from homeassistant_api import WebsocketClient # prevent circular import
|
|
611
|
+
|
|
612
|
+
if isinstance(self.domain._client, WebsocketClient):
|
|
613
|
+
raise NotImplementedError(
|
|
614
|
+
"WebsocketClient does not support async/await syntax."
|
|
615
|
+
)
|
|
616
|
+
try:
|
|
617
|
+
return await self.domain._client.async_trigger_service_with_response(
|
|
618
|
+
self.domain.domain_id,
|
|
619
|
+
self.service_id,
|
|
620
|
+
**service_data,
|
|
621
|
+
)
|
|
622
|
+
except RequestError:
|
|
623
|
+
return await self.domain._client.async_trigger_service(
|
|
624
|
+
self.domain.domain_id,
|
|
625
|
+
self.service_id,
|
|
626
|
+
**service_data,
|
|
627
|
+
)
|
|
628
|
+
|
|
629
|
+
def __call__(self, **service_data) -> Union[
|
|
630
|
+
Union[
|
|
631
|
+
Tuple[State, ...],
|
|
632
|
+
Tuple[Tuple[State, ...], dict[str, JSONType]],
|
|
633
|
+
dict[str, JSONType],
|
|
634
|
+
None,
|
|
635
|
+
],
|
|
636
|
+
Coroutine[
|
|
637
|
+
Any,
|
|
638
|
+
Any,
|
|
639
|
+
Union[Tuple[State, ...], Tuple[Tuple[State, ...], dict[str, JSONType]]],
|
|
640
|
+
],
|
|
641
|
+
]:
|
|
642
|
+
"""
|
|
643
|
+
Triggers the service associated with this object.
|
|
644
|
+
"""
|
|
645
|
+
assert (frame := inspect.currentframe()) is not None
|
|
646
|
+
assert (parent_frame := frame.f_back) is not None
|
|
647
|
+
try:
|
|
648
|
+
if inspect.iscoroutinefunction(
|
|
649
|
+
caller := gc.get_referrers(parent_frame.f_code)[0]
|
|
650
|
+
) or inspect.iscoroutine(caller):
|
|
651
|
+
return self.async_trigger(**service_data)
|
|
652
|
+
except IndexError: # pragma: no cover
|
|
653
|
+
pass
|
|
654
|
+
return self.trigger(**service_data)
|