hunch-engine 0.2.0__py3-none-any.whl
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.
- hunch/__init__.py +90 -0
- hunch/client.py +128 -0
- hunch/config.py +57 -0
- hunch/engine.py +240 -0
- hunch/loaders.py +80 -0
- hunch/model.py +67 -0
- hunch/phrasing.py +625 -0
- hunch/questions.py +79 -0
- hunch/resolution.py +138 -0
- hunch/resolver.py +351 -0
- hunch/round1.py +303 -0
- hunch/round2.py +374 -0
- hunch/scope.py +181 -0
- hunch/vocabulary.py +224 -0
- hunch_engine-0.2.0.dist-info/METADATA +9 -0
- hunch_engine-0.2.0.dist-info/RECORD +17 -0
- hunch_engine-0.2.0.dist-info/WHEEL +4 -0
hunch/phrasing.py
ADDED
|
@@ -0,0 +1,625 @@
|
|
|
1
|
+
"""Question wording, separated from question structure.
|
|
2
|
+
|
|
3
|
+
A Phrasebook holds every natural-language string Jev sees: verb phrasings, the Round 1
|
|
4
|
+
templates and flag instructions, Round 2 templates, parameter labels and Score rubric levels.
|
|
5
|
+
Question ids, state keys and option sentinels ("none", NO_MATCH) stay language-neutral.
|
|
6
|
+
EN is the default and what the golden corpus was tuned on; DE exists to test whether a German
|
|
7
|
+
home is better served by German questions.
|
|
8
|
+
"""
|
|
9
|
+
|
|
10
|
+
from __future__ import annotations
|
|
11
|
+
|
|
12
|
+
from collections.abc import Mapping
|
|
13
|
+
from dataclasses import dataclass, field
|
|
14
|
+
|
|
15
|
+
|
|
16
|
+
@dataclass(frozen=True)
|
|
17
|
+
class Phrasebook:
|
|
18
|
+
code: str
|
|
19
|
+
verb_question: str # {phrasing}
|
|
20
|
+
floor_question: str # {name}
|
|
21
|
+
area_question: str # {label}
|
|
22
|
+
domain_question: str # {domain}
|
|
23
|
+
flags: Mapping[str, str]
|
|
24
|
+
scene_question: str
|
|
25
|
+
condition_domain_question: str
|
|
26
|
+
verb_primary_question: str
|
|
27
|
+
area_primary_question: str
|
|
28
|
+
special_descriptions: Mapping[
|
|
29
|
+
str, str
|
|
30
|
+
] # several_verbs, no_verb, several_places, whole_home, no_place
|
|
31
|
+
exclusion_question: str # {name} {area}
|
|
32
|
+
target_question: str # {phrasing}
|
|
33
|
+
param_question: str # {param}
|
|
34
|
+
param_value_question: str # {param}: which literal number is the value
|
|
35
|
+
param_relative_question: str # Noul: is the number a change from the current value
|
|
36
|
+
param_inverted_question: str # Noul (covers): does the number say how far CLOSED
|
|
37
|
+
all_of_question: str # Noul: does the request mean every listed candidate
|
|
38
|
+
outside_scope_question: str # Noul: {phrasing} — a second action outside the named room?
|
|
39
|
+
include_question: (
|
|
40
|
+
str # Noul per candidate of a mixed-type sweep: {name} {type} {area} {phrasing}
|
|
41
|
+
)
|
|
42
|
+
cond_subject_question: str
|
|
43
|
+
cond_state_question: str
|
|
44
|
+
device_question: str
|
|
45
|
+
param_value_descriptions: Mapping[str, str] = field(
|
|
46
|
+
default_factory=dict
|
|
47
|
+
) # for the NO_MATCH option
|
|
48
|
+
verb_phrasing: Mapping[str, str] = field(default_factory=dict) # overrides Verb.phrasing
|
|
49
|
+
domain_labels: Mapping[str, str] = field(default_factory=dict) # overrides raw domain ids
|
|
50
|
+
param_labels: Mapping[str, str] = field(default_factory=dict) # overrides ParamSpec.name
|
|
51
|
+
score_levels: Mapping[str, tuple[str, ...]] = field(
|
|
52
|
+
default_factory=dict
|
|
53
|
+
) # overrides ScoreSpec.levels
|
|
54
|
+
# words people say for a verb, in this language (cue words, shown to Jev with the verb)
|
|
55
|
+
verb_synonyms: Mapping[str, tuple[str, ...]] = field(default_factory=dict)
|
|
56
|
+
# words people say for a domain, in this language
|
|
57
|
+
domain_synonyms: Mapping[str, tuple[str, ...]] = field(default_factory=dict)
|
|
58
|
+
# what one READS of a device type to check a condition (descriptions of condition_domain)
|
|
59
|
+
condition_domain_descriptions: Mapping[str, str] = field(default_factory=dict)
|
|
60
|
+
# domain -> state -> what that state means in everyday words (descriptions of cond_state)
|
|
61
|
+
condition_state_descriptions: Mapping[str, Mapping[str, str]] = field(default_factory=dict)
|
|
62
|
+
|
|
63
|
+
def phrasing_for(self, verb_name: str, default: str) -> str:
|
|
64
|
+
return self.verb_phrasing.get(verb_name, default)
|
|
65
|
+
|
|
66
|
+
def domain_label(self, domain: str) -> str:
|
|
67
|
+
return self.domain_labels.get(domain, domain)
|
|
68
|
+
|
|
69
|
+
def param_label(self, name: str) -> str:
|
|
70
|
+
return self.param_labels.get(name, name.replace("_", " "))
|
|
71
|
+
|
|
72
|
+
def levels_for(self, name: str, default: tuple[str, ...]) -> tuple[str, ...]:
|
|
73
|
+
return self.score_levels.get(name, default)
|
|
74
|
+
|
|
75
|
+
|
|
76
|
+
EN = Phrasebook(
|
|
77
|
+
code="en",
|
|
78
|
+
verb_question="Does the request ask to {phrasing}?",
|
|
79
|
+
floor_question="Does the request refer to the floor '{name}' or to all of it?",
|
|
80
|
+
area_question="Does the request refer to the area '{label}'?",
|
|
81
|
+
domain_question=(
|
|
82
|
+
"Does the request act on or ask about devices of type '{domain}' — as its target, not "
|
|
83
|
+
"merely a device it names as an exception ('except the fridge') or in a condition "
|
|
84
|
+
"('if the blinds are closed')?"
|
|
85
|
+
),
|
|
86
|
+
flags={
|
|
87
|
+
"collective": (
|
|
88
|
+
"Does the request target every matching device in its scope for at least one of its "
|
|
89
|
+
"actions — signaled by a plain plural (e.g. 'the kitchen lights'), or a word like "
|
|
90
|
+
"'all', 'both' or 'every' — rather than exactly one specific device?"
|
|
91
|
+
),
|
|
92
|
+
"names_specific": (
|
|
93
|
+
"Does the request point at ONE particular device by a name or label of its own — "
|
|
94
|
+
"such as 'Stehlampe', 'Spots', 'desk lamp', 'the TV' — rather than only by a kind of "
|
|
95
|
+
"device ('light', 'blinds', 'Rollos') together with a room, floor or a word like "
|
|
96
|
+
"'all'? A plural-looking proper name still counts as one device."
|
|
97
|
+
),
|
|
98
|
+
"has_exception": (
|
|
99
|
+
"Does the request exclude something, e.g. 'except', 'but not', 'apart from', "
|
|
100
|
+
"'other than'?"
|
|
101
|
+
),
|
|
102
|
+
"has_condition": (
|
|
103
|
+
"Does the request make the action depend on a condition about the state of the "
|
|
104
|
+
"world, e.g. 'if', 'when', 'unless', 'only if' — as opposed to merely excluding a "
|
|
105
|
+
"device with 'except' / 'but not' / 'außer', which is not a condition?"
|
|
106
|
+
),
|
|
107
|
+
"condition_numeric": (
|
|
108
|
+
"Does the request's condition compare a MEASURED VALUE against a number or threshold "
|
|
109
|
+
"— 'wenn es wärmer als 23 Grad ist', 'if it is below 40%', 'über 25 Grad', 'when "
|
|
110
|
+
"more than two people are home' — rather than checking a state such as open/closed, "
|
|
111
|
+
"on/off, home/away, dark/bright, running/stopped? No if there is no condition at all."
|
|
112
|
+
),
|
|
113
|
+
"has_timing": (
|
|
114
|
+
"Does the request ask to delay, schedule, sequence or time a device action, e.g. 'in "
|
|
115
|
+
"ten minutes', 'after', 'later', 'then', 'for 15 minutes'? Not a condition ('if', "
|
|
116
|
+
"'when the door is open', 'wenn') and not a mere mention of a time in a request that "
|
|
117
|
+
"controls no device (asking about tomorrow's weather)."
|
|
118
|
+
),
|
|
119
|
+
"is_destructive": (
|
|
120
|
+
"Would fulfilling the request cause irreversible, unsafe or security-relevant "
|
|
121
|
+
"effects beyond an ordinary lock, unlock, arm or disarm action (which are handled "
|
|
122
|
+
"separately) — for example, disabling safety equipment, or leaving the home open to "
|
|
123
|
+
"unauthorized entry?"
|
|
124
|
+
),
|
|
125
|
+
},
|
|
126
|
+
scene_question="Which scene or script does the request name, if any?",
|
|
127
|
+
condition_domain_question=(
|
|
128
|
+
"If the request contains a condition ('if', 'when', 'wenn', 'falls'): which device type "
|
|
129
|
+
"would one READ to check it? Pick the type of the thing being observed in the condition, "
|
|
130
|
+
"not the thing being controlled by the action."
|
|
131
|
+
),
|
|
132
|
+
verb_primary_question=(
|
|
133
|
+
"Which ONE action does the request primarily ask for? Compare the options: a word like "
|
|
134
|
+
"'auf' or 'zu' can look like several verbs but the request means one, and 'außer' / "
|
|
135
|
+
"'except' introduces an exception, not an action ('Licht aus außer …' is turn_off). "
|
|
136
|
+
"Choose 'several' "
|
|
137
|
+
"only if it clearly asks for more than one different action; 'none' if it asks for no "
|
|
138
|
+
"device action at all (a question about the world, a joke, a calculation, the time)."
|
|
139
|
+
),
|
|
140
|
+
area_primary_question=(
|
|
141
|
+
"Which ONE room, area or floor does the request refer to? Choose 'several' if it names "
|
|
142
|
+
"more than one, 'whole home' for 'alles', 'everything', 'überall', 'im ganzen Haus', and "
|
|
143
|
+
"'none' if no place is named or implied (a bare 'Licht aus' names none)."
|
|
144
|
+
),
|
|
145
|
+
special_descriptions={
|
|
146
|
+
"several_verbs": "more than one distinct action is requested",
|
|
147
|
+
"no_verb": "no device action is requested at all",
|
|
148
|
+
"several_places": "two or more rooms or floors are named",
|
|
149
|
+
"whole_home": "the whole house is meant",
|
|
150
|
+
"no_place": "no room, area or floor is named or implied",
|
|
151
|
+
"no_condition": (
|
|
152
|
+
"the request has no condition, or its condition is about none of these device "
|
|
153
|
+
"types (the time of day, the weather forecast, something a person said)"
|
|
154
|
+
),
|
|
155
|
+
"no_condition_state": (
|
|
156
|
+
"the condition is not about a state of this device — it compares a number or "
|
|
157
|
+
"threshold, or refers to a time or the weather"
|
|
158
|
+
),
|
|
159
|
+
"no_condition_subject": (
|
|
160
|
+
"none of the listed devices is the thing the condition observes — e.g. it is about "
|
|
161
|
+
"darkness, rain or presence and nothing here measures that"
|
|
162
|
+
),
|
|
163
|
+
},
|
|
164
|
+
exclusion_question=(
|
|
165
|
+
"The request in `request` names an exception — something that must NOT be affected. "
|
|
166
|
+
"Is the candidate named '{name}' in area '{area}' that exception?"
|
|
167
|
+
),
|
|
168
|
+
target_question="Which single device does the request want to {phrasing}?",
|
|
169
|
+
param_question="What {param} does the request ask for?",
|
|
170
|
+
param_value_question=(
|
|
171
|
+
"Which of these numbers from the request is the {param} to set? Choose none of them if "
|
|
172
|
+
"the number means something else (a time, a count, the weather, another device)."
|
|
173
|
+
),
|
|
174
|
+
param_relative_question=(
|
|
175
|
+
"Is that number a CHANGE relative to the current value — 'by 2 degrees warmer', '20% "
|
|
176
|
+
"quieter', 'um 20% dunkler' — as opposed to the position or value to END AT? "
|
|
177
|
+
"('15% zu', 'auf 15%', 'to 22 degrees' all name an end value, not a change.)"
|
|
178
|
+
),
|
|
179
|
+
all_of_question=(
|
|
180
|
+
"Looking at `candidates`: does the request mean ALL of these devices (a plural like "
|
|
181
|
+
"'die Rollos', 'the lights', or only a place named with no particular device — `scope` "
|
|
182
|
+
"says which floors or rooms the request named, with their aliases), rather than one "
|
|
183
|
+
"particular device among them?"
|
|
184
|
+
),
|
|
185
|
+
outside_scope_question=(
|
|
186
|
+
"The devices listed in `candidates` are NOT of the kind or in the place the request "
|
|
187
|
+
"refers to — they only came up because nothing closer matched. Does the request "
|
|
188
|
+
"nevertheless contain an instruction to {phrasing} that applies to devices like these "
|
|
189
|
+
"(e.g. 'turn off the kitchen lights and close the blinds' — the blinds are a separate "
|
|
190
|
+
"action)? Answer no if the action was meant for something else (another room, a "
|
|
191
|
+
"vacuum, a blind) or this verb just echoes a word like 'zu', 'auf' or 'start'."
|
|
192
|
+
),
|
|
193
|
+
include_question=(
|
|
194
|
+
"The request means several devices at once. Is '{name}' ({type}, {area}) one of the "
|
|
195
|
+
"devices it asks to {phrasing}? Answer no for a device of a kind the request did not "
|
|
196
|
+
"mean — e.g. a fridge plug when 'the kitchen lights' were asked for."
|
|
197
|
+
),
|
|
198
|
+
param_inverted_question=(
|
|
199
|
+
"For blinds or shutters only: does the request give that number as how far CLOSED they "
|
|
200
|
+
"should be — '15% zu', '15% geschlossen', 'closed 15%' — rather than the usual position "
|
|
201
|
+
"where 'auf 15%', 'to 15%', '15% open' all mean 15% open?"
|
|
202
|
+
),
|
|
203
|
+
param_value_descriptions={
|
|
204
|
+
"none of these": (
|
|
205
|
+
"the numbers mean something else: a time, a count, the weather, another device"
|
|
206
|
+
)
|
|
207
|
+
},
|
|
208
|
+
cond_subject_question="Which device is the request's condition about?",
|
|
209
|
+
cond_state_question="Which state must that device be in for the request's condition to hold?",
|
|
210
|
+
device_question="Which device does the request refer to?",
|
|
211
|
+
verb_synonyms={
|
|
212
|
+
"turn_on": ("on", "switch on", "turn on", "start"),
|
|
213
|
+
"turn_off": ("off", "switch off", "turn off", "kill"),
|
|
214
|
+
"open": ("open", "up", "raise"),
|
|
215
|
+
"close": ("close", "down", "lower", "shut"),
|
|
216
|
+
"set_position": ("halfway", "percent", "%", "position"),
|
|
217
|
+
"set_brightness": ("dim", "brighter", "darker", "brightness"),
|
|
218
|
+
"set_temperature": ("degrees", "warmer", "cooler", "temperature"),
|
|
219
|
+
"set_volume": ("louder", "quieter", "volume"),
|
|
220
|
+
"lock": ("lock",),
|
|
221
|
+
"unlock": ("unlock",),
|
|
222
|
+
"query_state": ("is …?", "how warm", "how much", "which … are"),
|
|
223
|
+
"activate": ("scene", "run the script"),
|
|
224
|
+
},
|
|
225
|
+
condition_domain_descriptions={
|
|
226
|
+
"sensor": "a measured value is read: temperature, humidity, brightness, power, air quality",
|
|
227
|
+
"binary_sensor": (
|
|
228
|
+
"an open/closed, detected/clear or present/away state is read: doors, windows, "
|
|
229
|
+
"motion, presence, darkness, rain, water leaks"
|
|
230
|
+
),
|
|
231
|
+
"climate": "a thermostat, heating or air conditioner: whether it runs and in which mode",
|
|
232
|
+
"cover": "whether blinds, shutters, curtains or a garage door are open or closed",
|
|
233
|
+
"light": "whether a light is on or off",
|
|
234
|
+
"switch": "whether a switch or plug is on or off",
|
|
235
|
+
"fan": "whether a fan is on or off",
|
|
236
|
+
"media_player": "whether a TV or speaker is playing, paused or off",
|
|
237
|
+
"lock": "whether a door is locked or unlocked",
|
|
238
|
+
"alarm_control_panel": "whether the alarm is armed or disarmed",
|
|
239
|
+
"person": "whether someone is at home or away",
|
|
240
|
+
"vacuum": "whether the robot vacuum is cleaning, docked or idle",
|
|
241
|
+
"humidifier": "whether a humidifier is on or off",
|
|
242
|
+
},
|
|
243
|
+
condition_state_descriptions={
|
|
244
|
+
"binary_sensor": {
|
|
245
|
+
"on": (
|
|
246
|
+
"active — a door or window IS open, motion or presence IS detected, it IS dark, "
|
|
247
|
+
"wet or occupied"
|
|
248
|
+
),
|
|
249
|
+
"off": (
|
|
250
|
+
"inactive — a door or window is closed, no motion or presence, it is bright, dry "
|
|
251
|
+
"or empty"
|
|
252
|
+
),
|
|
253
|
+
},
|
|
254
|
+
"cover": {
|
|
255
|
+
"open": "open, up",
|
|
256
|
+
"closed": "closed, down",
|
|
257
|
+
"opening": "moving up right now",
|
|
258
|
+
"closing": "moving down right now",
|
|
259
|
+
},
|
|
260
|
+
"climate": {
|
|
261
|
+
"heat": "heating",
|
|
262
|
+
"cool": "cooling — the air conditioner is running",
|
|
263
|
+
"auto": "running in automatic mode",
|
|
264
|
+
"off": "switched off, not running",
|
|
265
|
+
},
|
|
266
|
+
"lock": {"locked": "locked", "unlocked": "unlocked", "jammed": "jammed, stuck"},
|
|
267
|
+
"media_player": {
|
|
268
|
+
"playing": "playing",
|
|
269
|
+
"paused": "paused",
|
|
270
|
+
"idle": "on but playing nothing",
|
|
271
|
+
"off": "switched off",
|
|
272
|
+
},
|
|
273
|
+
"person": {"home": "at home", "not_home": "away, not at home"},
|
|
274
|
+
},
|
|
275
|
+
domain_synonyms={
|
|
276
|
+
"light": ("light", "lights", "lamp", "lamps", "lighting"),
|
|
277
|
+
"cover": (
|
|
278
|
+
"blind",
|
|
279
|
+
"blinds",
|
|
280
|
+
"shade",
|
|
281
|
+
"shades",
|
|
282
|
+
"curtain",
|
|
283
|
+
"curtains",
|
|
284
|
+
"shutter",
|
|
285
|
+
"shutters",
|
|
286
|
+
"garage door",
|
|
287
|
+
"awning",
|
|
288
|
+
),
|
|
289
|
+
"switch": ("switch", "switches", "plug", "plugs", "socket", "sockets", "outlet", "outlets"),
|
|
290
|
+
"media_player": ("tv", "television", "speaker", "speakers", "music", "radio", "stereo"),
|
|
291
|
+
"fan": ("fan", "fans"),
|
|
292
|
+
"climate": (
|
|
293
|
+
"thermostat",
|
|
294
|
+
"heating",
|
|
295
|
+
"heater",
|
|
296
|
+
"air conditioning",
|
|
297
|
+
"air conditioner",
|
|
298
|
+
"ac",
|
|
299
|
+
"climate",
|
|
300
|
+
),
|
|
301
|
+
"lock": ("lock", "locks", "door lock"),
|
|
302
|
+
"vacuum": ("vacuum", "robot vacuum", "hoover"),
|
|
303
|
+
"humidifier": ("humidifier",),
|
|
304
|
+
"alarm_control_panel": ("alarm", "alarm system"),
|
|
305
|
+
},
|
|
306
|
+
)
|
|
307
|
+
|
|
308
|
+
DE = Phrasebook(
|
|
309
|
+
code="de",
|
|
310
|
+
verb_question="Verlangt die Anfrage, {phrasing}?",
|
|
311
|
+
floor_question=(
|
|
312
|
+
"Bezieht sich die Anfrage auf das Stockwerk '{name}' oder auf das ganze Stockwerk?"
|
|
313
|
+
),
|
|
314
|
+
area_question="Bezieht sich die Anfrage auf den Raum bzw. Bereich '{label}'?",
|
|
315
|
+
domain_question=(
|
|
316
|
+
"Wirkt die Anfrage auf Geräte der Art '{domain}' oder fragt sie danach — als ihr Ziel, "
|
|
317
|
+
"nicht bloß als Gerät, das sie als Ausnahme ('außer dem Kühlschrank') oder in einer "
|
|
318
|
+
"Bedingung ('wenn die Rollos zu sind') nennt?"
|
|
319
|
+
),
|
|
320
|
+
flags={
|
|
321
|
+
"collective": (
|
|
322
|
+
"Zielt die Anfrage bei mindestens einer ihrer Aktionen auf alle passenden Geräte in "
|
|
323
|
+
"ihrem Bereich ab — erkennbar an einem einfachen Plural (z. B. 'die Lichter in der "
|
|
324
|
+
"Küche') oder an Wörtern wie 'alle', 'beide' oder 'jedes' — statt auf genau ein "
|
|
325
|
+
"bestimmtes Gerät?"
|
|
326
|
+
),
|
|
327
|
+
"names_specific": (
|
|
328
|
+
"Meint die Anfrage EIN bestimmtes Gerät mit einem eigenen Namen oder einer eigenen "
|
|
329
|
+
"Bezeichnung — etwa 'Stehlampe', 'Spots', 'Schreibtischlampe', 'der Fernseher' — statt "
|
|
330
|
+
"nur eine Geräteart ('Licht', 'Rollos') zusammen mit einem Raum, Stockwerk oder einem "
|
|
331
|
+
"Wort wie 'alle'? Ein Eigenname in Pluralform zählt trotzdem als ein Gerät."
|
|
332
|
+
),
|
|
333
|
+
"has_exception": (
|
|
334
|
+
"Schließt die Anfrage etwas aus, z. B. mit 'außer', 'aber nicht', 'abgesehen von', "
|
|
335
|
+
"'bis auf'?"
|
|
336
|
+
),
|
|
337
|
+
"has_condition": (
|
|
338
|
+
"Macht die Anfrage die Aktion von einer Bedingung über den Zustand der Welt abhängig, "
|
|
339
|
+
"z. B. 'wenn', 'falls', 'sobald', 'nur wenn' — im Gegensatz zum bloßen Ausschließen "
|
|
340
|
+
"eines Geräts mit 'außer' / 'aber nicht', was keine Bedingung ist?"
|
|
341
|
+
),
|
|
342
|
+
"condition_numeric": (
|
|
343
|
+
"Vergleicht die Bedingung der Anfrage einen MESSWERT mit einer Zahl oder Schwelle — "
|
|
344
|
+
"'wenn es wärmer als 23 Grad ist', 'unter 40 %', 'über 25 Grad', 'wenn mehr als zwei "
|
|
345
|
+
"Personen zu Hause sind' — statt einen Zustand zu prüfen wie offen/geschlossen, "
|
|
346
|
+
"an/aus, zu Hause/abwesend, dunkel/hell, läuft/steht? Nein, wenn es gar keine "
|
|
347
|
+
"Bedingung gibt."
|
|
348
|
+
),
|
|
349
|
+
"has_timing": (
|
|
350
|
+
"Verlangt die Anfrage, eine Geräteaktion zu verzögern, zu planen, zeitlich zu steuern "
|
|
351
|
+
"oder zu befristen, z. B. 'in zehn Minuten', 'nachher', 'später', 'danach', 'für 15 "
|
|
352
|
+
"Minuten'? Keine Bedingung ('wenn', 'falls', 'sobald die Tür offen ist') und keine "
|
|
353
|
+
"bloße Zeitangabe in einer Anfrage, die kein Gerät steuert (Wetter von morgen)."
|
|
354
|
+
),
|
|
355
|
+
"is_destructive": (
|
|
356
|
+
"Hätte die Erfüllung der Anfrage unumkehrbare, unsichere oder sicherheitsrelevante "
|
|
357
|
+
"Folgen, die über ein gewöhnliches Absperren, Aufsperren, Scharfschalten oder "
|
|
358
|
+
"Entschärfen hinausgehen (diese werden getrennt behandelt) — zum Beispiel das "
|
|
359
|
+
"Abschalten von Sicherheitseinrichtungen oder ein Haus, das Unbefugten offen steht?"
|
|
360
|
+
),
|
|
361
|
+
},
|
|
362
|
+
scene_question="Welche Szene oder welches Skript nennt die Anfrage, falls überhaupt?",
|
|
363
|
+
condition_domain_question=(
|
|
364
|
+
"Falls die Anfrage eine Bedingung enthält ('wenn', 'falls', 'sobald'): Welche Geräteart "
|
|
365
|
+
"würde man ABLESEN, um sie zu prüfen? Wähle die Art der beobachteten Sache in der "
|
|
366
|
+
"Bedingung, nicht die der gesteuerten."
|
|
367
|
+
),
|
|
368
|
+
verb_primary_question=(
|
|
369
|
+
"Welche EINE Aktion verlangt die Anfrage in erster Linie? Vergleiche die Optionen: ein "
|
|
370
|
+
"Wort wie 'auf' oder 'zu' kann nach mehreren Verben aussehen, gemeint ist eines. Wähle "
|
|
371
|
+
"'several' nur, wenn eindeutig mehrere verschiedene Aktionen verlangt sind; 'none', wenn "
|
|
372
|
+
"gar keine Geräteaktion verlangt ist (eine Frage über die Welt, ein Witz, eine Rechnung, "
|
|
373
|
+
"die Uhrzeit)."
|
|
374
|
+
),
|
|
375
|
+
area_primary_question=(
|
|
376
|
+
"Auf welchen EINEN Raum, Bereich oder welches Stockwerk bezieht sich die Anfrage? Wähle "
|
|
377
|
+
"'several' bei mehreren, 'whole home' bei 'alles', 'überall', 'im ganzen Haus', und "
|
|
378
|
+
"'none', wenn kein Ort genannt oder gemeint ist (ein bloßes 'Licht aus' nennt keinen)."
|
|
379
|
+
),
|
|
380
|
+
special_descriptions={
|
|
381
|
+
"several_verbs": "mehr als eine verschiedene Aktion ist verlangt",
|
|
382
|
+
"no_verb": "es ist gar keine Geräteaktion verlangt",
|
|
383
|
+
"several_places": "zwei oder mehr Räume oder Stockwerke sind genannt",
|
|
384
|
+
"whole_home": "das ganze Haus ist gemeint",
|
|
385
|
+
"no_place": "kein Raum, Bereich oder Stockwerk ist genannt oder gemeint",
|
|
386
|
+
"no_condition": (
|
|
387
|
+
"die Anfrage hat keine Bedingung, oder ihre Bedingung betrifft keine dieser "
|
|
388
|
+
"Gerätearten (Uhrzeit, Wettervorhersage, etwas, das jemand gesagt hat)"
|
|
389
|
+
),
|
|
390
|
+
"no_condition_state": (
|
|
391
|
+
"die Bedingung betrifft keinen Zustand dieses Geräts — sie vergleicht eine Zahl "
|
|
392
|
+
"oder Schwelle oder meint eine Uhrzeit oder das Wetter"
|
|
393
|
+
),
|
|
394
|
+
"no_condition_subject": (
|
|
395
|
+
"keines der aufgezählten Geräte ist das, was die Bedingung beobachtet — z. B. geht "
|
|
396
|
+
"es um Dunkelheit, Regen oder Anwesenheit und nichts hier misst das"
|
|
397
|
+
),
|
|
398
|
+
},
|
|
399
|
+
exclusion_question=(
|
|
400
|
+
"Die Anfrage in `request` nennt eine Ausnahme — etwas, das NICHT betroffen sein darf. "
|
|
401
|
+
"Ist der Kandidat namens '{name}' im Raum '{area}' diese Ausnahme?"
|
|
402
|
+
),
|
|
403
|
+
target_question="Auf welches einzelne Gerät bezieht sich die Anfrage ({phrasing})?",
|
|
404
|
+
param_question="Welche {param} verlangt die Anfrage?",
|
|
405
|
+
param_value_question=(
|
|
406
|
+
"Welche dieser Zahlen aus der Anfrage ist die einzustellende {param}? Wähle keine davon, "
|
|
407
|
+
"wenn die Zahl etwas anderes meint (eine Uhrzeit, eine Anzahl, das Wetter, ein anderes "
|
|
408
|
+
"Gerät)."
|
|
409
|
+
),
|
|
410
|
+
param_relative_question=(
|
|
411
|
+
"Ist diese Zahl eine ÄNDERUNG gegenüber dem aktuellen Wert — 'um 2 Grad wärmer', '20% "
|
|
412
|
+
"leiser', 'um 20% dunkler' — im Gegensatz zu der Position bzw. dem Wert, bei dem es ENDEN "
|
|
413
|
+
"soll? ('15% zu', 'auf 15%', 'auf 22 Grad' nennen alle einen Endwert, keine Änderung.)"
|
|
414
|
+
),
|
|
415
|
+
all_of_question=(
|
|
416
|
+
"Mit Blick auf `candidates`: meint die Anfrage ALLE diese Geräte (ein Plural wie 'die "
|
|
417
|
+
"Rollos', 'die Lichter', oder nur ein Ort ohne bestimmtes Gerät — `scope` nennt die "
|
|
418
|
+
"Stockwerke oder Räume der Anfrage samt Aliasen), statt eines bestimmten Geräts darunter?"
|
|
419
|
+
),
|
|
420
|
+
outside_scope_question=(
|
|
421
|
+
"Die Geräte in `candidates` sind NICHT von der Art oder an dem Ort, auf die sich die "
|
|
422
|
+
"Anfrage bezieht — sie kamen nur ins Spiel, weil nichts Näheres passte. Enthält die "
|
|
423
|
+
"Anfrage trotzdem eine Anweisung, {phrasing}, die für solche Geräte gilt (z. B. "
|
|
424
|
+
"'Küchenlicht aus und Rollos zu' — die Rollos sind eine eigene Aktion)? Antworte nein, "
|
|
425
|
+
"wenn die Aktion etwas anderem galt (einem anderen Raum, einem Staubsauger, einem Rollo) "
|
|
426
|
+
"oder das Verb bloß ein Wort wie 'zu', 'auf' oder 'starte' widerspiegelt."
|
|
427
|
+
),
|
|
428
|
+
include_question=(
|
|
429
|
+
"Die Anfrage meint mehrere Geräte auf einmal. Gehört '{name}' ({type}, {area}) zu den "
|
|
430
|
+
"Geräten, die sie {phrasing} soll? Antworte nein bei einer Geräteart, die nicht gemeint "
|
|
431
|
+
"war — z. B. eine Kühlschrank-Steckdose, wenn 'die Küchenlichter' verlangt wurden."
|
|
432
|
+
),
|
|
433
|
+
param_inverted_question=(
|
|
434
|
+
"Nur bei Rollos oder Jalousien: gibt die Anfrage die Zahl als Anteil GESCHLOSSEN an — "
|
|
435
|
+
"'15% zu', '15% geschlossen' — statt als übliche Position, bei der 'auf 15%', 'zu 15% "
|
|
436
|
+
"offen', '15% offen' alle 15% offen bedeuten?"
|
|
437
|
+
),
|
|
438
|
+
param_value_descriptions={
|
|
439
|
+
"none of these": (
|
|
440
|
+
"die Zahlen meinen etwas anderes: eine Uhrzeit, eine Anzahl, das Wetter, ein "
|
|
441
|
+
"anderes Gerät"
|
|
442
|
+
)
|
|
443
|
+
},
|
|
444
|
+
cond_subject_question="Um welches Gerät geht es in der Bedingung der Anfrage?",
|
|
445
|
+
cond_state_question=(
|
|
446
|
+
"In welchem Zustand muss dieses Gerät sein, damit die Bedingung der Anfrage erfüllt ist?"
|
|
447
|
+
),
|
|
448
|
+
device_question="Auf welches Gerät bezieht sich die Anfrage?",
|
|
449
|
+
verb_phrasing={
|
|
450
|
+
"turn_on": (
|
|
451
|
+
"ein bestimmtes Gerät oder bestimmte Geräte direkt einzuschalten "
|
|
452
|
+
"(nicht über eine Szene oder ein Skript)"
|
|
453
|
+
),
|
|
454
|
+
"turn_off": (
|
|
455
|
+
"ein bestimmtes Gerät oder bestimmte Geräte direkt auszuschalten "
|
|
456
|
+
"(nicht über eine Szene oder ein Skript)"
|
|
457
|
+
),
|
|
458
|
+
"set_brightness": "einzustellen, wie hell ein Licht ist (dimmen, heller, dunkler)",
|
|
459
|
+
"open": "Rollos, Jalousien, Vorhänge oder ein Garagentor zu öffnen bzw. hochzufahren",
|
|
460
|
+
"close": (
|
|
461
|
+
"Rollos, Jalousien, Vorhänge oder ein Garagentor zu schließen bzw. herunterzufahren"
|
|
462
|
+
),
|
|
463
|
+
"set_position": (
|
|
464
|
+
"Rollos, Jalousien oder Vorhänge auf eine bestimmte Zwischenposition zu fahren, etwa "
|
|
465
|
+
"halb oder auf einen Prozentwert, statt nur ganz auf oder ganz zu"
|
|
466
|
+
),
|
|
467
|
+
"lock": "eine Tür abzusperren bzw. zu verriegeln (nicht aufsperren)",
|
|
468
|
+
"unlock": "eine Tür aufzusperren bzw. zu entriegeln (nicht absperren)",
|
|
469
|
+
"set_temperature": "eine Zieltemperatur einzustellen oder es wärmer oder kälter zu machen",
|
|
470
|
+
"set_volume": (
|
|
471
|
+
"die Lautstärke eines Lautsprechers oder Fernsehers zu ändern (lauter, leiser)"
|
|
472
|
+
),
|
|
473
|
+
"media_pause": "die Wiedergabe zu pausieren",
|
|
474
|
+
"media_play": "die Wiedergabe zu starten oder fortzusetzen",
|
|
475
|
+
"arm": "die Alarmanlage scharf zu schalten",
|
|
476
|
+
"disarm": "die Alarmanlage zu entschärfen",
|
|
477
|
+
"activate": "eine Szene zu aktivieren oder ein Skript beim Namen zu starten",
|
|
478
|
+
"query_state": (
|
|
479
|
+
"zu erfahren, ob etwas an, aus, offen, geschlossen oder gesperrt ist, "
|
|
480
|
+
"oder welchen Wert es hat"
|
|
481
|
+
),
|
|
482
|
+
},
|
|
483
|
+
domain_labels={
|
|
484
|
+
"light": "Licht / Lampen",
|
|
485
|
+
"switch": "Schalter / Steckdosen",
|
|
486
|
+
"cover": "Rollos, Jalousien, Markisen oder Tore",
|
|
487
|
+
"media_player": "Fernseher / Lautsprecher / Medienwiedergabe",
|
|
488
|
+
"fan": "Lüfter / Ventilatoren",
|
|
489
|
+
"climate": "Klimaanlage / Heizung / Thermostat",
|
|
490
|
+
"sensor": "Sensoren / Messwerte (Temperatur, Luftfeuchtigkeit …)",
|
|
491
|
+
"binary_sensor": "Kontakte und Melder (Fenster, Türen, Bewegung, Präsenz)",
|
|
492
|
+
"humidifier": "Luftbefeuchter",
|
|
493
|
+
"vacuum": "Staubsaugerroboter",
|
|
494
|
+
"todo": "Listen / Erinnerungen",
|
|
495
|
+
"lock": "Türschlösser",
|
|
496
|
+
"alarm_control_panel": "Alarmanlage",
|
|
497
|
+
"scene": "Szenen",
|
|
498
|
+
"script": "Skripte",
|
|
499
|
+
},
|
|
500
|
+
param_labels={
|
|
501
|
+
"brightness_pct": "Helligkeit",
|
|
502
|
+
"position": "Position (wie weit offen)",
|
|
503
|
+
"temperature": "Temperatur",
|
|
504
|
+
"volume_level": "Lautstärke",
|
|
505
|
+
},
|
|
506
|
+
score_levels={
|
|
507
|
+
"brightness_pct": ("aus", "sehr gedimmt", "gedimmt", "mittel", "hell", "volle Helligkeit"),
|
|
508
|
+
"position": (
|
|
509
|
+
"ganz geschlossen",
|
|
510
|
+
"größtenteils geschlossen",
|
|
511
|
+
"halb offen",
|
|
512
|
+
"größtenteils offen",
|
|
513
|
+
"ganz offen",
|
|
514
|
+
),
|
|
515
|
+
"temperature": (
|
|
516
|
+
"kalt (16 °C)",
|
|
517
|
+
"kühl (18 °C)",
|
|
518
|
+
"mild (20 °C)",
|
|
519
|
+
"warm (22 °C)",
|
|
520
|
+
"heiß (24 °C)",
|
|
521
|
+
),
|
|
522
|
+
"volume_level": ("stumm", "leise", "mittel", "laut", "maximal"),
|
|
523
|
+
},
|
|
524
|
+
verb_synonyms={
|
|
525
|
+
"turn_on": ("an", "ein", "einschalten", "anmachen", "aufdrehen", "starte"),
|
|
526
|
+
"turn_off": ("aus", "ausschalten", "ausmachen", "abdrehen", "aus außer …"),
|
|
527
|
+
"open": ("auf", "öffnen", "hoch", "hochfahren", "rauf"),
|
|
528
|
+
"close": ("zu", "schließen", "runter", "runterfahren", "herunter"),
|
|
529
|
+
"set_position": ("halb", "prozent", "%", "position"),
|
|
530
|
+
"set_brightness": ("dimmen", "heller", "dunkler", "helligkeit"),
|
|
531
|
+
"set_temperature": ("grad", "wärmer", "kälter", "temperatur"),
|
|
532
|
+
"set_volume": ("lauter", "leiser", "lautstärke"),
|
|
533
|
+
"lock": ("absperren", "zusperren", "verriegeln"),
|
|
534
|
+
"unlock": ("aufsperren", "entriegeln"),
|
|
535
|
+
"query_state": ("ist …?", "wie warm", "wie viel", "welche … sind"),
|
|
536
|
+
"activate": ("szene", "skript starten"),
|
|
537
|
+
},
|
|
538
|
+
condition_domain_descriptions={
|
|
539
|
+
"sensor": "ein Messwert wird abgelesen: Temperatur, Luftfeuchtigkeit, Helligkeit, Leistung",
|
|
540
|
+
"binary_sensor": (
|
|
541
|
+
"ein Zustand offen/geschlossen, erkannt/frei oder anwesend/abwesend wird abgelesen: "
|
|
542
|
+
"Türen, Fenster, Bewegung, Präsenz, Dunkelheit, Regen, Wasser"
|
|
543
|
+
),
|
|
544
|
+
"climate": "Thermostat, Heizung oder Klimaanlage: ob sie läuft und in welchem Modus",
|
|
545
|
+
"cover": "ob Rollos, Jalousien, Vorhänge oder ein Tor offen oder geschlossen sind",
|
|
546
|
+
"light": "ob ein Licht an oder aus ist",
|
|
547
|
+
"switch": "ob ein Schalter oder eine Steckdose an oder aus ist",
|
|
548
|
+
"fan": "ob ein Lüfter an oder aus ist",
|
|
549
|
+
"media_player": "ob ein Fernseher oder Lautsprecher spielt, pausiert oder aus ist",
|
|
550
|
+
"lock": "ob eine Tür abgesperrt oder aufgesperrt ist",
|
|
551
|
+
"alarm_control_panel": "ob die Alarmanlage scharf oder unscharf ist",
|
|
552
|
+
"person": "ob jemand zu Hause oder abwesend ist",
|
|
553
|
+
"vacuum": "ob der Staubsaugerroboter saugt, in der Station steht oder wartet",
|
|
554
|
+
"humidifier": "ob ein Luftbefeuchter an oder aus ist",
|
|
555
|
+
},
|
|
556
|
+
condition_state_descriptions={
|
|
557
|
+
"binary_sensor": {
|
|
558
|
+
"on": (
|
|
559
|
+
"aktiv — eine Tür oder ein Fenster IST offen, Bewegung oder Präsenz IST erkannt, "
|
|
560
|
+
"es IST dunkel, nass oder belegt"
|
|
561
|
+
),
|
|
562
|
+
"off": (
|
|
563
|
+
"inaktiv — Tür oder Fenster geschlossen, keine Bewegung oder Präsenz, es ist "
|
|
564
|
+
"hell, trocken oder leer"
|
|
565
|
+
),
|
|
566
|
+
},
|
|
567
|
+
"cover": {
|
|
568
|
+
"open": "offen, oben",
|
|
569
|
+
"closed": "geschlossen, unten",
|
|
570
|
+
"opening": "fährt gerade hoch",
|
|
571
|
+
"closing": "fährt gerade runter",
|
|
572
|
+
},
|
|
573
|
+
"climate": {
|
|
574
|
+
"heat": "heizt",
|
|
575
|
+
"cool": "kühlt — die Klimaanlage läuft",
|
|
576
|
+
"auto": "läuft im Automatikmodus",
|
|
577
|
+
"off": "ausgeschaltet, läuft nicht",
|
|
578
|
+
},
|
|
579
|
+
"lock": {"locked": "abgesperrt", "unlocked": "aufgesperrt", "jammed": "verklemmt"},
|
|
580
|
+
"media_player": {
|
|
581
|
+
"playing": "spielt",
|
|
582
|
+
"paused": "pausiert",
|
|
583
|
+
"idle": "an, spielt aber nichts",
|
|
584
|
+
"off": "ausgeschaltet",
|
|
585
|
+
},
|
|
586
|
+
"person": {"home": "zu Hause", "not_home": "abwesend, nicht zu Hause"},
|
|
587
|
+
},
|
|
588
|
+
domain_synonyms={
|
|
589
|
+
"light": ("licht", "lichter", "lampe", "lampen", "leuchte", "leuchten", "beleuchtung"),
|
|
590
|
+
"cover": (
|
|
591
|
+
"rollo",
|
|
592
|
+
"rollos",
|
|
593
|
+
"jalousie",
|
|
594
|
+
"jalousien",
|
|
595
|
+
"rolladen",
|
|
596
|
+
"rollladen",
|
|
597
|
+
"rollläden",
|
|
598
|
+
"vorhang",
|
|
599
|
+
"vorhänge",
|
|
600
|
+
"markise",
|
|
601
|
+
"markisen",
|
|
602
|
+
"raffstore",
|
|
603
|
+
"garagentor",
|
|
604
|
+
),
|
|
605
|
+
"switch": ("schalter", "steckdose", "steckdosen", "stecker", "steckerleiste"),
|
|
606
|
+
"media_player": (
|
|
607
|
+
"fernseher",
|
|
608
|
+
"tv",
|
|
609
|
+
"lautsprecher",
|
|
610
|
+
"musik",
|
|
611
|
+
"radio",
|
|
612
|
+
"box",
|
|
613
|
+
"boxen",
|
|
614
|
+
"anlage",
|
|
615
|
+
),
|
|
616
|
+
"fan": ("lüfter", "ventilator", "ventilatoren", "abluft"),
|
|
617
|
+
"climate": ("klimaanlage", "klima", "heizung", "thermostat", "klimagerät"),
|
|
618
|
+
"lock": ("schloss", "türschloss"),
|
|
619
|
+
"vacuum": ("staubsauger", "saugroboter", "roboter"),
|
|
620
|
+
"humidifier": ("luftbefeuchter", "befeuchter"),
|
|
621
|
+
"alarm_control_panel": ("alarmanlage", "alarm"),
|
|
622
|
+
},
|
|
623
|
+
)
|
|
624
|
+
|
|
625
|
+
PHRASEBOOKS: dict[str, Phrasebook] = {"en": EN, "de": DE}
|