From d043a6fbc6f827281fbf5cbd726ef15898df1f45 Mon Sep 17 00:00:00 2001 From: JarbasAi Date: Tue, 2 Jun 2026 09:25:50 +0100 Subject: [PATCH 01/10] feat: add HF dataset loader, intent expansion, and locale export MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Introduce ovos_spec_tools.datasets — a new module for loading, expanding, and exporting OVOS-INTENT-2 templates from HuggingFace datasets: * load_dataset_templates() — load from hass-intent-templates, intents-for-eval, or massive-templates via datasets library * expand_hf_template() — resolve , (a|b), [x] into concrete utterances using the existing expansion.py engine * export_to_locale() — write .intent / .voc / .entity files into a standard OVOS-INTENT-2 locale directory tree Add example scripts for the full dataset generation pipeline: * convert_hassil_intents.py — Home Assistant hassil → OVOS locale * export_hf_dataset.py — locale → multi-config HF dataset * generate_entities.py — auto-generate missing .entity files * reexport_recursive.py — recursively resolve nested refs * reexport_uniform.py — uniform list schema for expansions * hf_dataset.py — unified CLI for all three supported datasets Update pyproject.toml with optional 'datasets' dependency. Add 17 tests covering config resolution, normalization, expansion, and locale export. --- AGENTS.md | 60 + TODO.md | 15 + examples/APPENDIX.md | 373 ++++++ examples/convert_hassil_intents.py | 1813 ++++++++++++++++++++++++++++ examples/export_hf_dataset.py | 340 ++++++ examples/generate_entities.py | 476 ++++++++ examples/hf_dataset.py | 95 ++ examples/locale_to_hf_dataset.py | 224 ++++ examples/reexport_recursive.py | 109 ++ examples/reexport_uniform.py | 70 ++ ovos_spec_tools/__init__.py | 17 + ovos_spec_tools/datasets.py | 352 ++++++ pyproject.toml | 3 + test/test_datasets.py | 207 ++++ 14 files changed, 4154 insertions(+) create mode 100644 AGENTS.md create mode 100644 TODO.md create mode 100644 examples/APPENDIX.md create mode 100644 examples/convert_hassil_intents.py create mode 100644 examples/export_hf_dataset.py create mode 100644 examples/generate_entities.py create mode 100644 examples/hf_dataset.py create mode 100644 examples/locale_to_hf_dataset.py create mode 100644 examples/reexport_recursive.py create mode 100644 examples/reexport_uniform.py create mode 100644 ovos_spec_tools/datasets.py create mode 100644 test/test_datasets.py diff --git a/AGENTS.md b/AGENTS.md new file mode 100644 index 00000000..03e38594 --- /dev/null +++ b/AGENTS.md @@ -0,0 +1,60 @@ +# AGENTS.md — ovos-spec-tools + +Reference implementation of the OVOS formal specifications: a dependency-light Python library and `ovos-spec-lint` CLI providing the conformant primitives those specs describe (sentence template expansion, locale resource loading, dialog/prompt rendering, language-tag matching, bus message envelope, and a locale linter). + +## Setup + +```bash +pip install -e . # core, zero runtime dependencies +pip install -e .[langcodes] # adds smart language fallback for LocaleResources / language.py +pip install -e .[test] # pytest + langcodes +``` + +Requires Python 3.10+. + +## Test + +```bash +pytest test +``` + +The `test/` suite covers every module. `langcodes` must be installed (it is in the `test` extra) or language-fallback paths degrade to exact-match. + +## Lint/Typecheck + +`ruff` runs in CI via the shared `lint.yml` workflow. No local ruff config is committed; no typechecker is configured. There is no `.pre-commit-config.yaml`. + +## Layout + +`ovos_spec_tools/` is a flat package, one module per spec primitive: + +- `expansion.py` — OVOS-INTENT-1 sentence template expander (`expand`, `MalformedTemplate`). +- `resources.py` — OVOS-INTENT-2 locale loader (`LocaleResources`), `iter_locale_dirs`, `find_lang_dir`, `keyword_form`, `utterance_contains`, `strip_samples`, resource-file readers, role constants. +- `dialog.py` — OVOS-INTENT-2 §4.2 dialog renderer (`render`, `DialogRenderer`, `UnfilledSlot`). +- `prompt.py` — OVOS-INTENT-2 §4.4 `.prompt` renderer (`render_prompt`, `PromptRenderer`). +- `language.py` — OVOS-INTENT-2 §2.2 language-tag matching (`standardize_lang`, `lang_distance`, `lang_matches`, `closest_lang`); uses `langcodes` when present. +- `message.py` — OVOS-MSG-1 bus message envelope (`Message`, `forward`/`reply`/`response`, `serialize`, `DEFAULT_SESSION_ID`). +- `lint.py` — locale linter (`lint_locale`, `Finding`, `main`); console entry point `ovos-spec-lint`. +- `version.py` — version string (do not edit). + +`test/` mirrors the modules. `examples/` holds runnable scripts plus sample/dirty locale fixtures and a HuggingFace-dataset exporter. `docs/` is a zero-to-hero guide. + +Entry point: console script `ovos-spec-lint` only (`[project.scripts]`). This is **not** an OPM/OVOS plugin or skill — there are no `ovos.plugin.*` or skill entry-point groups. + +## Conventions + +- Branches: `dev` (work) and `master` (stable). NEVER use `main`. +- Never edit `version.py`; gh-automations bumps semver from conventional-commit prefixes (`feat:`, `fix:`, `feat!:`). +- New repos private by default. +- Commit identity: `JarbasAi `. +- Reference `OpenVoiceOS/gh-automations` reusable workflows at `@dev`. +- No Neon / `neon-*` references. +- No meta-commentary (no history, dates, or "design mistake" framing) in docs, commits, code comments, or PRs — describe current state only. +- CI is provided by `OpenVoiceOS/gh-automations`. + +## Gotchas + +- Core has zero runtime dependencies by design; keep it that way. `langcodes` is optional — guard its use so language resolution degrades to exact-match without it, never imports unconditionally. +- `license_check.yml` runs `warn_only: true`: the published PyPI release predates the SPDX classifier so the package self-audits as unknown; do not treat that warning as a regression. +- `lint.py` understands a `--spec-version` (0–3) gate to flag features newer than a target spec; keep role/feature additions wired into that gate. +- Generated artifacts (`build/`, `*.egg-info/`, `.coverage`) appear on disk but are gitignored and untracked — do not commit them. diff --git a/TODO.md b/TODO.md new file mode 100644 index 00000000..dabcbd5b --- /dev/null +++ b/TODO.md @@ -0,0 +1,15 @@ +# TODO — ovos-spec-tools + +## Open issues + +- [ ] #2 Dependency Dashboard (Renovate bot) + +## Gaps + +- [ ] `license_check.yml` runs in `warn_only` mode because the PyPI release predates the SPDX license classifier; re-publish so the package self-audits cleanly, then drop `warn_only`. +- [ ] No `coverage_source` enforcement: `coverage.yml` sets `min_coverage: 0`; raise once the suite is judged complete. +- [ ] Build/egg-info/.coverage artifacts present in the working tree (gitignored, untracked) — keep the tree clean before packaging. + +## Code TODOs + +None found. diff --git a/examples/APPENDIX.md b/examples/APPENDIX.md new file mode 100644 index 00000000..723f38ee --- /dev/null +++ b/examples/APPENDIX.md @@ -0,0 +1,373 @@ +# APPENDIX — hassil → OVOS-INTENT-2 conversion + +This appendix formalises the mapping between the [hassil] intent grammar +(used by Home Assistant's [OHF-Voice/intents] corpus) and the +OVOS-INTENT-1 / OVOS-INTENT-2 resource model. It documents the rules +implemented by `examples/convert_hassil_intents.py` and the trade-offs +the script makes when the two formats disagree. + +This is **not** a normative OVOS specification. It is a reference for +anyone writing a bidirectional translator between the two ecosystems. + +[hassil]: https://github.com/home-assistant/hassil +[OHF-Voice/intents]: https://github.com/OHF-Voice/intents + +--- + +## 1. Grammar correspondence + +| hassil | OVOS-INTENT-1 | Notes | +|--------|---------------|-------| +| `[opt]` | `[opt]` | identical — optional block | +| `(a\|b)` | `(a\|b)` | identical — alternation | +| `A\|B\|C` (top-level) | `(A\|B\|C)` | hassil allows top-level alternation without an enclosing paren; OVOS treats those as literal `\|` chars, so we wrap in a virtual outer group during path-counting and enumeration | +| `{slot}` | `{slot}` | identical — slot reference | +| `` | `` | identical token shape, but semantics differ — see §4 | +| `(a; b; c)` | enumerated `(a b c\|a c b\|…)` | hassil's permutation operator has no OVOS counterpart | +| `{list:slot}` | `{slot}` | list-name suppressed; entity stored under the slot name | +| `{@list}` | `{list}` | compact capture, no OVOS analogue | +| `{list:@cap}` | `{cap}` | capture name becomes the slot name | +| `\X` | `\X` | identical backslash escape | + +The four core tokens — optional, alternation, slot, reference — are +syntactically shared. Permutations and the four list-naming variants +are hassil-only and are normalised away during conversion. + +## 2. Resource correspondence + +| hassil source | OVOS resource | Role | +|---------------|---------------|------| +| `sentences//.yaml` — `intents:` block | `.intent` | matched templates | +| `sentences//_common.yaml` — `lists:` with `values:` | `.entity` | slot value sets | +| `sentences//_common.yaml` — `lists:` with `range:` | `.entity` | enumerated integer slot values | +| `sentences//_common.yaml` — `lists:` with `wildcard:` | _(none)_ | free-form capture slot, no value file | +| `sentences//_common.yaml` — `expansion_rules:` | inlined, or `.voc` (see §4) | slot-free vocabularies | +| `responses//.yaml` | `.dialog` | response phrase sets | + +### 2.1 Rule scoping + +Hassil's `expansion_rules:` can appear at three scopes: + + 1. `_common.yaml` — global to the language. + 2. Per-file `expansion_rules:` block — scoped to that intent file. + 3. Per-data-block `expansion_rules:` inside an `intents:` data block + — scoped to a single set of samples. + +The converter **hoists all three scopes into a single global pool** +before running the promotion pipeline (§4). Without this hoist, +narrow-scoped helper rules like `` (Catalan timer status) +or `` (per-intent shopping list wrapper) stayed inlined and +contributed to cartesian explosions. `_common.yaml` definitions take +precedence on name collision. + +A per-block or per-file rule that *shadows* a globally promoted rule +is skipped — reinstating the inlined body would undo the `.voc` +promotion and reintroduce the very cartesian explosion the promotion +was meant to fix. + +## 3. Naming + +OVOS-INTENT-2 requires lowercase ASCII base names. Hassil intent names +are CamelCase; rule and slot names in non-English locales contain +language-script characters (Spanish `habitación`, German `öffnen`, +Cyrillic `включити`). + + * Intent names → snake-cased: `HassTurnOn` → `hass_turn_on.intent`. + * Rule / slot / list names → run through a slug filter that + (1) NFKD-folds accented Latin to ASCII (`habitación` → `habitacion`, + `öffnen` → `offnen`), (2) replaces remaining non-ASCII with `_`, + (3) for names that fold to empty (CJK, Arabic, Devanagari, …) falls + back to a stable `x_<8hex>` hash slug. + +For non-Latin scripts the `x_` slugs are unreadable; the +canonical-name table (§5) can override them with English topic names. + +## 4. Expansion rules — `.voc` versus inline + +A hassil `` is a parameterless macro that substitutes a template +fragment at the call site. OVOS-INTENT-2's `` reference is a +**lookup** into a `.voc` resource — a slot-free vocabulary file. The +semantics line up only when the rule body is slot-free. + +The converter classifies each rule into one of four buckets: + +### 4.1 Inlined + +Rules whose enumeration has fewer than `PROMOTE_RULE_THRESHOLD = 2` +paths (i.e. literal, no alternation or optional) are inlined at every +call site. Trivial. + +### 4.2 Auto-promoted to `.voc` + +When a rule's body is slot-free *and* its Cartesian enumeration fits +within `MAX_PROMOTED_VALUES = 2048`, the rule is **promoted to a +vocabulary file**: + + rule: = "(close|shut|lower) [up|down]" + sample: [the] {name} + output: close.voc ← enumerated values, one per line + hass_close.intent: [the] {name} + +The `` reference is preserved verbatim in the `.intent` sample; +the `.voc` file holds the alternation flattened to one literal phrase +per line. This is the OVOS-INTENT-2 canonical form and lint-friendly +(no nested alternation to enumerate). + +### 4.3 Force-promoted to free-form capture slot + +A small hardcoded set of rules (`timer_duration`, `timer_start`, +`area`, `name`, `floor`) have bodies dense with nested optionals, +alternations, and `{slot}` references. Inlining them produces samples +with 10⁹-path expansions; they can't be enumerated to a `.voc` because +they carry inner slots; and their matched text is a structured artefact +that downstream code wants to parse. + +These are **force-promoted to free-form capture slots**: + + rule: = "[| ]{area}|…" + sample: turn on [the] {name} in + output: hass_turn_on.intent: turn on [the] {name} in {area} + _(no value-set file — runtime captures arbitrary text)_ + +`` similarly collapses to `{timer_duration}` and a +duration-resolver in the skill parses the captured text. + +### 4.4 Multi-source canonical aggregation + +When multiple local-language rules canonicalise to the same English +concept (see §5), each keeps its own `.voc` with local synonyms and +a **parent `.voc`** named after the canonical English concept lists +them as references: + + es/cancel.voc: fr/turn_off.voc: + + + + es/cancela.voc: fr/eteins.voc: + cancela arrête + cancelación arrêter + cancelar coupe + couper + … + +This is the OVOS "vocabulary of vocabularies" pattern. The `.intent` +samples reference the canonical parent (``, ``) and +the parent transitively pulls in all sibling local-language children. + +Two safety guards apply: + + * **Intra-group reference preservation** — when a child rule's body + references a sibling (e.g. French `eteins` body contains + ``, both mapping to `turn_off`), the sibling + reference is kept local to avoid a cycle through the canonical + parent. + * **Collision avoidance** — if the canonical English name already + exists as another rule in the same language (e.g. `ro/brightness` + is a slot-wrapper while `ro/luminozitatea` is the noun), the + mapping is dropped rather than silently overwriting the existing + rule. The local name is kept. + +## 5. Canonical rule-name table + +`CANONICAL_RULE_NAMES[] = {local_name: english_name}` maps each +language's hassil rule names to canonical English topic names so that +the resulting locale tree is portable. Coverage (per-language, in the +checked-in table) currently includes: + + * **Romance**: es, ca, fr, gl, it, pt, pt-BR, pt-PT, ro + * **Germanic**: de, de-CH, nl, da, sv, nb, lb, is + * **Slavic**: cs, sk, sl, hr, sr-Latn, pl + * **Hellenic / Celtic / Hungarian**: el, cy, hu + * **Uralic**: fi + * **Sino-Tibetan**: zh-CN, zh-HK, zh-TW + * **Other**: ar, vi, th + +Concepts mapped per language vary, but commonly include: + + * actions — `turn_on`, `turn_off`, `open`, `close`, `start`, `stop`, + `cancel`, `pause`, `resume`, `play`, `skip`, `raise`, `lower`, + `increase`, `decrease`, `add`, `remove`, `set`, `broadcast`, + `clean`, `lock`, `unlock`, `mow`, `vacuum` + * objects — `light`, `lights`, `fan`, `fans`, `door`, `doors`, + `window`, `gate`, `cover`, `shutter`, `curtain`, `garage`, + `device`, `sensor`, `script`, `scene` + * concepts — `battery`, `battery_level`, `brightness`, `volume`, + `temperature`, `color`, `here`, `now`, `all`, `everywhere`, + `home`, `previous`, `next`, `any`, `which`, `what_is`, `where_is`, + `how_many`, `how_much`, `percent`, `degrees` + * units — `hour_unit`, `minute_unit`, `second_unit`, `meter_unit` + +### 5.1 Kept local-language (intentional) + +Per the "keep local only when the concept is language-specific" rule, +these stay under their hassil-original names: + + * **Articles** — `le/la/les` (fr), `der/die/das` (de), + `el/la/els/les` (ca), `il/lo/gli` (it), `el/la` (es). + * **Declension / case markers** — German `artikel_bestimmt`, + `artikel_unbestimmt`, `possessivpronom_mein`, + `possessivpronom_unser`; Finnish `*_taivutus`; Hungarian + `*_ragok`, `*_szavak`; Catalan `preposicio_singular_masc`, + `pronom_plural`; Romanian prepositions `de/din/in/la`. + * **Politeness particles** — Thai `polite_prefix*`, + `polite_suffix*`. + +These have no useful English mapping because they encode grammar +specific to the source language. + +### 5.2 Children of multi-source canonical groups + +A side-effect of §4.4: when multiple local rules collapse to one +canonical English name, the *children* are kept under their hassil +names so the canonical parent can reference them. The user-visible +behaviour is that browsing the `.voc` directory shows e.g. + + es/cancel.voc ← canonical parent + es/cancela.voc ← child (kept local, referenced by parent) + es/cancelar_temporizador.voc + +These local-named files are intentional and exist solely to support +the canonical parent. + +## 6. Sample validation — OVOS-INTENT-1 §3.6 / §5.5 + +OVOS-INTENT-1 imposes three constraints hassil does not: + + * **No adjacent slots** — `{a} {b}` is forbidden; a literal word must + separate any two slots. Hassil allows it. + * **No repeated slot names per sample** — `{a} and {a}` is forbidden. + Hassil allows it. + * **Uniform slot signature** — every sample in one `.intent` (and + every phrase in one `.dialog`) must declare the same `{slot}` set. + Hassil allows mixed signatures across data blocks. + +The converter materialises the slot-only structure of each template +(literal text replaced with whitespace, slot markers preserved) and +runs the Cartesian enumeration against that. For each rejected +enumeration path the converter falls back to **path-level salvage**: + + 1. Enumerate the full template (literal text included). + 2. Drop paths that violate §3.6 (adjacency or repeated slots). + 3. Group the survivors by slot signature. + 4. Emit only the group with the maximal signature (matches the + compact-form sig used by sibling samples). + +If every path is invalid the sample is logged as `all_paths_invalid` +and dropped. + +Dialog phrase sets recovered from `{% if … %}` branches handle §5.5 +differently — see §8.2 — because dropping a branch means losing one +conditional response, which is worse than emitting an extra file. + +## 7. Safety caps + +Hassil grammars use rule inlining and permutations that combine into +exponential string explosion. The converter guards every stage: + +| Cap | Value | Purpose | +|-----|-------|---------| +| `MAX_PERM_ELEMS` | 5 | permutations of >5 elements collapse to literal concatenation | +| `MAX_SAMPLE_BYTES` | 4 KiB | drop any rewritten template larger than this | +| `MAX_SAMPLE_PATHS` | 2048 | drop samples whose Cartesian expansion exceeds this | +| `MAX_ENTITY_VALUES` | 2000 | cap on `range:` list materialisation | +| `MAX_RULE_BYTES` | 16 KiB | short-circuit fixed-point rule inlining if a body blows up | +| `PROMOTE_RULE_THRESHOLD` | 2 | any rule with ≥1 alt/opt becomes a `.voc` file | +| `MAX_PROMOTED_VALUES` | 2048 | rules whose enumeration exceeds this stay inlined | + +Every output file is written line-by-line; the dedupe set for the +currently-open file is the only state held in memory. + +## 8. Response normalisation + +Hassil responses are Jinja2 templates. The converter handles a small +subset and drops the rest. + +### 8.1 Variable substitution + +| Jinja | OVOS dialog | Notes | +|-------|-------------|-------| +| `{{ var }}` | `{var}` | bare variable | +| `{{ slots.X }}` | `{X}` | slot access — most common pattern | +| `{{ state.X }}` | `{X}` | state-object access | +| `{{ query.X }}` | `{X}` | query-object access (matched lists, …) | +| `{{ X \| filter }}` | `{X}` | filters dropped — OVOS has no Jinja filter pipeline | + +### 8.2 Control flow + +`{% set ... %}` blocks are stripped (their bindings can't be carried +into a stateless dialog phrase). + +`{% if ... %} A {% elif ... %} B {% else %} C {% endif %}` is +**decomposed into one phrase per branch**. Each (response key, Jinja +branch) pair becomes its own `.dialog` file: + + .dialog default key, no Jinja + _branch_.dialog default key, branch N + _.dialog non-default response key, no Jinja + __branch_.dialog non-default key, branch N + +The skill calls the file whose name corresponds to its runtime +condition — mirroring the original Jinja branch selection. + +### 8.3 Unresolvable + +Anything else falls under `jinja_template_unresolvable` and is logged +to the audit TSV. Typical examples: + + * dict lookups — `{{ months[slots.date.month] }}` + * deep object navigation — `{{ slots.date.day }}`, `{{ slots.time.hour }}` + * `format()` calls — `{{ '{0:02d}'.format(slots.time.minute) }}` + * array operations — `{{ query.matched | length }}` (length filter + is dropped but the bare `query.matched` substitution survives), + `{% for name in matched %} … {% endfor %}` (loop body discarded) + +These idioms have no static template equivalent — they would need a +Python helper resolver at render time, which is out of scope for a +template-to-template converter. + +## 9. Audit log + +Every hard failure is recorded in +`examples/convert_hassil_intents.skipped.tsv` with columns: + + lang language code (ISO 639-1 / BCP-47) + kind "sample" | "response" | "entity" + intent hassil intent name + reason stable identifier (cartesian_explosion, adjacent_slots, + repeated_slot, jinja_template_unresolvable, + all_paths_invalid, sample_too_large_after_*, …) + original the raw hassil source string + +Only **hard failures** are logged — slots that degrade gracefully to +free-form capture (wildcard lists, undefined lists referenced by a +slot) are not, because the corresponding `.intent` line is still +emitted and the slot remains functional at runtime. + +## 10. Round-trip considerations + +The conversion is **lossy** and **one-way**: + + * Permutations are enumerated and lose their compact form. + * Hassil filters and Jinja control flow other than `{% if %}` are + dropped. + * Path-salvage emits literal enumerations in place of the original + compact template — the `{slot}` placeholders survive but the + optional grouping does not. + * §5.5 enforcement picks the maximal-signature group for *samples*, + dropping sub-signature branches; for *dialogs* sub-signature + branches land in `_branch_` companion files instead. + * The canonical-name table folds language-local rule names to + English topic names — the reverse direction would need the same + table read backwards (one canonical name → multiple local names). + +A reverse converter (OVOS → hassil) would need to re-discover common +alternations across sample lines to rebuild compact templates, run the +canonical map in reverse to recover local rule names, and reconstruct +the per-block scoping. None of that is trivial. + +The audit TSV is intended to make every loss visible: anyone using +the converter as part of a build pipeline can grep the TSV for +specific (lang, intent) pairs and either accept the loss, rewrite the +source by hand, or extend the converter with another targeted +heuristic (a new entry in `CANONICAL_RULE_NAMES`, a new +`FORCE_PROMOTE` entry, or a new Jinja substitution rule). diff --git a/examples/convert_hassil_intents.py b/examples/convert_hassil_intents.py new file mode 100644 index 00000000..ff644c57 --- /dev/null +++ b/examples/convert_hassil_intents.py @@ -0,0 +1,1813 @@ +"""Convert OHF-Voice/intents (hassil) into an OVOS-INTENT-2 locale tree. + +A demonstration of the OVOS-INTENT-1 ↔ hassil grammar overlap noted in +the architecture appendix: `[opt]`, `(a|b)`, `{slot}` and `` are +shared; the remaining hassil-specific forms have to be normalised: + + {list:slot} → {slot} (entity stored under the slot name) + {@list} → {list} (compact capture form) + {list:@cap} → {cap} (capture; treated as slot for matching) + (a; b; c) → (a b c|a c b|…) (permutation → enumerated alternatives) + (X) → X (single-branch group is a no-op) + → inlined (rules expanded at use sites) + +Lists become `.entity` files: + + values: → literal value set + range: → enumerated integer set + wildcard: → skipped (free-form slot at runtime) + +Responses become `.dialog` after stripping hassil's Jinja; responses +with surviving Jinja (e.g. `state.domain` lookups) are skipped. + +**Safety caps** — hassil grammars contain permutations and nested rule +references that combine into exponential string explosion. The script +guards against OOM with: + + * permutations of >5 elements (>120 orderings) collapse to a literal + concatenation rather than enumerating; + * rewritten samples larger than ``MAX_SAMPLE_BYTES`` are skipped; + * each output file is written line-by-line with no full-corpus buffer; + * if the output file already exists it is left alone (resumable). + +This is an illustrative example, not a library util. The transformations +are textual and best-effort; a production tool would parse hassil +properly. + +Run: + pip install pyyaml + git clone https://github.com/OHF-Voice/intents /tmp/ohf-intents + python examples/convert_hassil_intents.py /tmp/ohf-intents en /tmp/ovos-locale +""" +from __future__ import annotations + +import hashlib +import itertools +import re +import sys +import unicodedata +from pathlib import Path + +import yaml + +MAX_PERM_ELEMS = 5 # 5! = 120 orderings — anything above is collapsed +MAX_SAMPLE_BYTES = 4 * 1024 # skip rewritten samples larger than 4 KiB +MAX_SAMPLE_PATHS = 20000 # skip samples whose Cartesian expansion is too big +MAX_ENTITY_VALUES = 2000 # cap .entity file size (range lists) +MAX_RULE_BYTES = 16 * 1024 # skip inlining a rule whose body exceeds this +PROMOTE_RULE_THRESHOLD = 2 # any rule with ≥1 alt/opt becomes a `.voc` file +MAX_PROMOTED_VALUES = 2048 # don't promote a rule whose enumeration is huger + +# Rules to force-promote to free-form capture slots even though their +# bodies contain `{slot}` references (so they can't be enumerated into a +# value set). +# +# * `timer_duration` / `timer_start` inline into a 3-branch nested +# grammar with multiple sub-slots and optionals — 10⁹-path samples. +# * `area` / `name` / `floor` are slot wrappers that thread the actual +# `{area}` / `{name}` / `{floor}` slots through optional article and +# preposition syntax that varies dramatically across languages. In +# German and Dutch in particular the inflection tables multiply with +# every optional in the host template, accounting for ~65% of the +# cartesian explosions in the audit log. +FORCE_PROMOTE: set[str] = { + "timer_duration", "timer_start", + "area", "name", "floor", +} + +# Per-language rule-name translations. Each language's hassil _common.yaml +# uses local-language rule names (`apaga`, `enciende`, `ausschalten`, …). +# When we promote a rule to a .voc file, we'd rather have a canonical +# English topic name across all languages so the resulting locale tree +# is portable (e.g. all languages' "turn off" vocabulary lives at +# `turn_off.voc`). +# +# Coverage is best-effort — only the high-impact verb concepts are +# mapped here. Languages or rules without an entry fall through to the +# original local-language name. Extend this table as needed. +CANONICAL_RULE_NAMES: dict[str, dict[str, str]] = { + # Spanish --------------------------------------------------------- + "es": { + # actions + "abre": "open", "cierra": "close", + "apaga": "turn_off", "enciende": "turn_on", + "sube": "raise", "baja": "lower", + "aumenta": "increase", "disminuye": "decrease", "reduce": "decrease", + "establece": "set", "cambia": "set", + "establece_abre_cierra": "set_or_adjust", + "establece_sube_baja": "set_or_adjust", + "continua": "resume", "continúa": "resume", "vuelve": "resume", + "resume": "resume", + "añadir": "add", "anadir": "add", "add": "add", + "elimina": "remove", "quita": "remove", "resta": "remove", + "cancela": "cancel", "cancelar_temporizador": "cancel", + "para": "stop", "pausa": "pause", + "reproduce": "play", "inicia": "start", + "salta": "skip", "ejecuta": "run", "crea": "create", + "mide": "measure", "dime": "tell_me", + # concepts + "anterior": "previous", "ahora": "now", + "casa": "home", "aqui": "here", "de_aqui": "from_here", + "todos": "all", "todas_partes": "everywhere", + "temp": "temp", "porciento": "percent", + "temporizador": "timer", "temporizadores": "timers", + "cerrables": "closable", "luces": "lights", "puerta": "door", + "pista": "track", "vol": "volume", "habitacion": "room", + "se_encuentra": "where_is", "cual_es": "what_is", + "cuantos": "how_many", "otra_vez": "again", + # local-only (Spanish-specific contraction) + "en_el": "in_the", + }, + # Catalan --------------------------------------------------------- + "ca": { + # actions + "obre": "open", "tanca": "close", + "engega": "turn_on", "apaga": "turn_off", + "engega_stt_typo": "turn_on", "apaga_stt_typo": "turn_off", + "al_stt_typo": "turn_on", "llums_typo": "lights", + "puja": "raise", "pujar": "raise", "baixar": "lower", + "afegir": "add", "elimina": "remove", + "cancelar": "cancel", "cancela": "cancel", + "pausar": "pause", "reactiva": "resume", + "reproduir": "play", "reproduir_again": "play", + "torna_a": "play_again", "configura": "set", + # concepts + "actual": "now", "anterior": "previous", + "again": "again", "hora": "time", + "temporitzador": "timer", "bateria": "battery", + "tot": "all", "element": "item", "volum": "volume", + "llums": "lights", "esta_hiha": "is_there", + "quin_es": "what_is", "how_much": "how_much", + "can_you": "can_you", + "seguent": "next", "percent": "percent", + # local-only (Catalan grammar markers) + "nombre_indeterminat": "nombre_indeterminat", + "objectes_amb_clau": "objectes_amb_clau", + "preposicio": "preposicio", "preposicio_base": "preposicio_base", + "preposicio_base_singular": "preposicio_base_singular", + "preposicio_base_plural": "preposicio_base_plural", + "preposicio_singular": "preposicio_singular", + "preposicio_singular_masc": "preposicio_singular_masc", + "pronom": "pronom", "pronom_singular": "pronom_singular", + "pronom_plural": "pronom_plural", + }, + # French ---------------------------------------------------------- + "fr": { + # actions + "ouvre": "open", "ferme": "close", + "allume": "turn_on", "éteins": "turn_off", "eteins": "turn_off", + "eteins_dirty": "turn_off", "mets_dirty": "turn_on", + "augmente": "increase", "diminue": "decrease", + "monte": "raise", "baisse": "lower", "descend": "lower", + "ajoute": "add", "supprime": "remove", "enleve": "remove", + "annonce": "broadcast", "arrete": "stop", "active": "start", + "demarre": "start", "cree": "create", "nettoie": "clean", + "regle": "set", "mets": "set", "reprends": "resume", + "verrouille": "lock", "deverrouille": "unlock", + "lecture": "play", "lis": "play", + "tond": "mow", "eclaire": "light_up", + "renvoie": "return", "donnemoi": "give_me", + # concepts + "pourcent": "percent", "degres": "degrees", + "aujourdhui": "today", "completement": "completely", + "tous": "all", "partout": "everywhere", "ici": "here", + "lumiere": "light", "appareil": "device", "capteur": "sensor", + "fenetre": "window", "serrure": "lock_device", + "maison": "home", "pelouse": "lawn", "media": "media", + "piece": "room", "volume": "volume", + "ventilateur": "fan", "ventilateurs": "fans", + "minuteur": "timer", "minuteurs": "timers", + "estil": "is_it", "atil": "is_there", "yatil": "is_there_any", + "ou_je_suis": "where_am_i", "quel": "which", "quelest": "what_is", + "en_route": "en_route", "hour_unit": "hour_unit", + "minute_unit": "minute_unit", "second_unit": "second_unit", + "m_unit": "meter_unit", "s_unit": "second_unit", + # local-only (French grammar) + "le": "le", "au": "au", "mon": "mon", + "dans": "dans", "de": "de", + }, + # German ---------------------------------------------------------- + "de": { + # actions + "einschalten": "turn_on", "ausschalten": "turn_off", + "schalten": "switch_action", "machen": "do", + "öffnen": "open", "schließen": "close", + "schliessen": "close", "schliessen_end_of_sentence": "close", + "offnen_end_of_sentence": "open", "absperren": "lock", + "erhöhen": "increase", "verringern": "decrease", + "starten": "start", "starte": "start", + "starten_end_of_sentence": "start", + "stoppen": "stop", "stelle": "set", + "setze": "set", "setzen": "set", "setzen_end_of_sentence": "set", + "aktivieren": "activate", "ausfuehren": "run", + "entsperren": "unlock", "sperren": "lock", + "fahren": "drive", "wiederhole": "repeat", + "erstelle": "create", "leuchten_lassen": "turn_on", + "media_mute": "mute", "media_unmute": "unmute", + # concepts + "batterie": "battery", "ladestand": "battery_level", + "alle": "all", "hier": "here", "welche": "which", + "irgend": "any", "etwas": "any", "ding": "thing", + "fenster": "window", "tuer": "door", "tor": "gate", + "garage": "garage", "abdeckung": "cover", + "licht": "light", "lichter": "lights", "lichtes": "lights", + "lichtes_mit_artikel": "lights", "licht_ohne_artikel": "light", + "luefter": "fan", "rollladen": "shutter", + "co": "co", "co_sensor": "co_sensor", + "gas_sensor": "gas_sensor", "source_of_noise": "noise_source", + "im_zuhause": "at_home", "ist_wurde": "is_was", + "sind_wurden": "are_were", "wieviel": "how_much", + "naechster": "next", "vorheriger_letzter": "previous", + "skript": "script", "szene": "scene", "song": "song", + "media_type": "media_type", "erkannt": "detected", + "schloss": "lock_device", "timer_decrease": "timer_decrease", + "timer_cancel_end_of_sentence": "timer_cancel", + "timer_set_end_of_sentence": "timer_set", + "timer_decrease_end_of_sentence": "timer_decrease", + # German grouping aliases — all variants fold to one canonical + "alle_abdeckungen": "all_covers", "alle_garagen": "all_garages", + "alle_lichter": "all_lights", + "alle_lichter_ueberall": "all_lights_everywhere", + "alle_luefter": "all_fans", + "luefter_genitiv": "fan", "luefter_ueberall": "all_fans", + # local-only (German grammar) + "an": "an", "aus": "aus", "auf": "auf", "zu": "zu", + "artikel": "artikel", "artikel_bestimmt": "artikel_bestimmt", + "artikel_unbestimmt": "artikel_unbestimmt", + "alle_genitiv": "alle_genitiv", + "possessivpronom_mein": "possessivpronom_mein", + "possessivpronom_unser": "possessivpronom_unser", + "preposition": "preposition", + "from_the": "from_the", "to_the": "to_the", + }, + # Portuguese (BR) ------------------------------------------------- + "pt-BR": { + "abrir": "open", "fechar": "close", + "ligar": "turn_on", "desligar": "turn_off", + "aumentar": "increase", "diminuir": "decrease", + "adicionar": "add", "remover": "remove", + "retirar": "remove", "deletar": "remove", + "cancelar": "cancel", "pausar": "pause", + "iniciar": "start", "terminar": "stop", "completar": "complete", + "colocar": "put", "juntar": "add", "mudar": "set", + "tornar": "set", "retornar": "resume", + "alarme": "timer", "brilho": "brightness", + "cade": "where_is", "algum": "some", "qual": "which", + "casa": "home", "ventilador": "fan", + "temporizador": "timer", "temporizadores": "timers", + "esta": "is_at", "falta": "remaining", + "cortina": "curtain", "todas": "all", + "por": "by", + # local-only + "artigos": "artigos", + }, + # Portuguese (PT) ------------------------------------------------- + "pt-PT": { + "abrir": "open", "fechar": "close", + "ligar": "turn_on", "desligar": "turn_off", + "aumentar": "increase", "diminuir": "decrease", + "adicionar": "add", "remover": "remove", + "cancelar": "cancel", "pausar": "pause", + }, + # Italian --------------------------------------------------------- + "it": { + "apri": "open", "chiudi": "close", + "accendi": "turn_on", "spegni": "turn_off", + "aumenta": "increase", "diminuisci": "decrease", + "alza": "raise", "abbassa": "lower", + "metti": "put", "fai": "do", "annuncia": "broadcast", + "fermati": "stop", "avvia": "start", + "announce": "broadcast", "put": "put", "made": "made", + "triggered": "triggered", "to_do": "to_do", + "to_lock": "to_lock", "hello": "hello", + "home": "home", "climate": "climate", + "cover": "cover", "fan": "fan", "garage": "garage", + "light": "light", "lock": "lock_device", + "locked": "locked", "unlocked": "unlocked", + "opened": "opened", "closed": "closed", + "there_is": "there_is", "hour_unit": "hour_unit", + "minute_unit": "minute_unit", "second_unit": "second_unit", + "in_here": "here", + # local-only (Italian articles/prepositions) + "the": "the", "at": "at", "in": "in", "of": "of", + "to": "to", "from": "from", "onto": "onto", "on": "on", + "some": "some", "my": "my", + }, + # Dutch ----------------------------------------------------------- + "nl": { + "open": "open", "sluit": "close", + "open_action": "open", "close_action": "close", + "open_command": "open", "close_command": "close", + "close_command_suffix": "close", + "schakel_in": "turn_on", "schakel_uit": "turn_off", + "verhoog": "increase", "verlaag": "decrease", + "change": "set", "change_infinitive": "set", + "detect": "detect", "by": "by", "here": "here", + "brightness": "brightness", + "brightness_no_article": "brightness", + "light": "light", "light_no_article": "light", + "light_composed": "light", "room_light": "light", + "all_light": "all_lights", + "cover": "cover", "curtain": "curtain", "blind": "blind", + "awning": "awning", "shade": "shade", "shutter": "shutter", + "door": "door", "gate": "gate", "garage": "garage", + "window": "window", "fan": "fan", + "lock": "lock_device", "locked": "locked", "unlocked": "unlocked", + "closed": "closed", + "switch": "switch", "sensor": "sensor", + "media_item": "media_item", "timer": "timer", + "timer_named": "timer_named", "to": "to", + "warm": "warm", "what_is": "what_is", + "state": "state", "degrees": "degrees", + # local-only + "in": "in", "is": "is", "my": "my", + "numeric_value_set": "numeric_value_set", + }, + # Romanian -------------------------------------------------------- + "ro": { + "deschide": "open", "inchide": "close", + "porneste": "turn_on", "opreste": "turn_off", + "porneste_timer": "timer_set", "opreste_timer": "timer_cancel", + "reia_timer": "timer_resume", "suspenda_timer": "timer_pause", + "modifica_temperatura": "set_temperature", + "adauga": "add", "sterge": "remove", + "seteaza": "set", "ruleaza": "run", "reda": "play", + "aspira": "vacuum", + "adauga_amount_la_complement": "add_amount", + "scade_amount_din_complement": "subtract_amount", + "precedentul": "previous", "urmatorul": "next", + "numit": "named", "exista": "exists", "este": "is", + "detectat": "detected", "nedetectat": "not_detected", + "detectate": "detected", "nedetectate": "not_detected", + "cald": "warm", "frig": "cold", "cald_frig": "warm_cold", + "lumina": "light", "luminile": "lights", + "luminozitatea": "brightness", + "dispozitiv": "device", "dispozitive": "devices", + "fereastra": "window", "ferestrele": "windows", + "poarta": "gate", "portile": "gates", + "ventilatorul": "fan", "ventilatoarele": "fans", + "ventilatorului": "fan", "ventilatoarelor": "fans", + "usa": "door", "usile": "doors", + "intrerupatorul": "switch", "intrerupatoarele": "switches", + "incuietoarea": "lock_device", "incuietorile": "locks", + "incuiat": "locked", "incuiate": "locked", "descuiat": "unlocked", + "descuiate": "unlocked", + "deschis": "opened", "deschise": "opened", + "inchis": "closed", "inchise": "closed", + "pornit": "on", "pornite": "on", "oprit": "off", "oprite": "off", + "ore": "hours", "minute": "minutes", "secunde": "seconds", + "temporizatorul": "timer", "temporizatorului": "timer", + "toti": "all", "jumatate": "half", + "temperatura": "temperature", "temperatura_aerului": "air_temperature", + "de_aici": "from_here", "vreun": "any", "temporar": "temporary", + "pozitia": "position", "viteza": "speed", "volumul": "volume", + "la_suta": "percent", "maximum": "max", "minimum": "min", + "culoarea": "color", "care": "which", + "cat": "how_many", "cate": "how_many", "cat_quant": "how_much", + "pana_la": "until", "cu": "with", + # local-only + "de": "de", "in": "in", "din": "din", "la": "la", + }, + # Finnish --------------------------------------------------------- + "fi": { + "avaa": "open", "sulje": "close", + "kytke": "turn_on", "paalta": "turn_off", + "kaynnista": "start", "pysayta": "stop", + "kerro": "tell", + "kytkimet": "switches", "kytkin": "switch", + "laita": "put", "laite": "device", + "valo": "light", "valot": "lights", + "valaistus": "lighting", "valaistukset": "lights", + "kirkkaus": "brightness", + "kirkkaudeksi": "brightness", "kirkkaudelle": "brightness", + "kirkkauteen": "brightness", + "vari": "color", + "varit": "colors", "variin": "color", "vareihin": "colors", + "variksi": "color", "vareiksi": "colors", + "varille": "color", "vareille": "colors", "variltaan": "color", + "lampotila": "temperature", + "nyt": "now", "paikka": "place", + "kuinka": "how", "paljonko": "how_much", "montako": "how_many", + "kuuma": "hot", "kuumaa": "hot", "kuumaksi": "hot", + "kuuman": "hot", "kuumana": "hot", + "kylma": "cold", "kylmaa": "cold", "kylmaksi": "cold", + "kylman": "cold", "kylmana": "cold", + "jokin": "any", "jotkut": "some", "kaikki": "all", + "onko": "is_it", "ovatko": "are_they", + "tuuletin": "fan", "tuulettimet": "fans", + # local-only (Finnish case morphology) + "alue": "alue", "alueella": "alueella", "alueelle": "alueelle", + "alueelta": "alueelta", "alueen": "alueen", + "alueeseen": "alueeseen", "alueessa": "alueessa", + "alueesta": "alueesta", "alue_taivutus": "alue_taivutus", + "kirkkaus_taivutus": "kirkkaus_taivutus", + "laite_taivutus": "laite_taivutus", "vari_taivutus": "vari_taivutus", + "laitteelle": "laitteelle", "laitteelta": "laitteelta", + "laitteen": "laitteen", "laitteeseen": "laitteeseen", + "laitteessa": "laitteessa", "laitteesta": "laitteesta", + }, + # Galician -------------------------------------------------------- + "gl": { + "abre": "open", "pecha": "close", + "acende": "turn_on", "apaga": "turn_off", + "baixa": "lower", "sube": "raise", + "fecha_con_chave": "lock", + "cancela": "cancel", "salta": "skip", + "para": "stop", "pausa": "pause", + "engadir": "add", "elimina": "remove", + "poner": "put", "establece": "set", + "continua": "resume", "volve": "resume", + "reproduce": "play", "inicia": "start", + "anterior": "previous", "seguinte": "next", + "algun": "some", "todos": "all", "todas_partes": "everywhere", + "de_aqui": "from_here", "esta": "is", + "cal": "which", "cal_e": "what_is", "que_marca": "showing", + "porcento": "percent", + "temporizador": "timer", "temporizadores": "timers", + "crea": "create", "dime": "tell_me", + "luces": "lights", "casa": "home", "habitacion": "room", + "pista": "track", "abre_quitafeche": "set_or_adjust", + "outra_vez": "again", "pechables": "closable", + "establece_abre_pecha": "set_or_adjust", + "establece_sube_baixa": "set_or_adjust", + # local-only + "meu": "meu", "con": "con", "en": "en", "por": "por", + }, + # Slovak ---------------------------------------------------------- + "sk": { + # already mostly English in source + "previous_acc": "previous", "previous_nom": "previous", + "turn_on_activate": "turn_on", "turn_off_light": "turn_off", + "turn_on_light": "turn_on", + }, + # Hungarian ------------------------------------------------------- + "hu": { + "zar": "lock", + "futtat": "run", "foglalt": "busy", + "helyen": "at_place", "jelenlet": "presence", + "melyik": "which", "mennyi": "how_much", + "minden": "all", "mindenhol": "everywhere", + "nincs_otthon": "not_at_home", "otthon": "home", + "szemely": "person", "szenzor": "sensor", + "szinhomerseklet": "color_temperature", + "valaki": "somebody", "vane": "is_there", + "ventilator": "fan", "eszkoz": "device", + "fenyero": "brightness", "barmelyik": "any", + "open_close_dev": "set_or_adjust", + "open_dev": "open", "close_dev": "close", + # local-only (Hungarian morphology) + "area_ragok": "area_ragok", "area_szavak": "area_szavak", + "name_ragok": "name_ragok", "name_szavak": "name_szavak", + "ragok": "ragok", + "idojarashelyek": "idojarashelyek", "idojarasragok": "idojarasragok", + }, + # Norwegian Bokmål ------------------------------------------------ + "nb": { + "alle": "all", "apen": "open", "apne": "open", + "aktiver": "activate", "bryter": "switch", + "endre": "set", "garasje": "garage", + "gardin": "curtain", "gjenstar": "remaining", + "hvilke": "which", + "kald": "cold", "kaldt_varmt": "cold_warm", + "las": "lock", "lukk": "close", "lukket": "closed", + "lys": "light", "maleenhet": "measurement_unit", + "malt": "measured", "markise": "awning", + "noe": "any", + "persienne": "shutter", "port": "gate", + "programvare": "software", + "rullegardin": "roller_blind", "rykvarsler": "smoke_alarm", + "skodde": "shutter_cover", + "skru_av": "turn_off", "skru_pa": "turn_on", + "tilstand": "state", "varm": "warm", + "vifte": "fan", "vindu": "window", "dr": "door", + "i_pa": "in_on", + }, + # Czech ----------------------------------------------------------- + "cs": { + "aktivovat": "activate", "bude": "will_be", "byl": "was", + "casovac": "timer", "dalsi": "next", + "hodina": "hour", "jaky_je": "what_is", "je": "is", + "koncentrace": "concentration", "ktere": "which", + "minuta": "minute", "nastavit": "set", + "nektere": "some", "obecne_zmenit": "set", + "odemknout": "unlock", "otevrit": "open", + "oznam": "announce", "pojmenovany": "named", + "predchozi": "previous", "prejit": "skip", + "rozsvitit": "turn_on", "roztahnout": "open", + "sekunda": "second", "spustit": "start", + "svetla": "lights", "tady": "here", + "upravit": "set", "vsude": "everywhere", + "vypnout": "turn_off", "zamknout": "lock", + "zapnout": "turn_on", "zatahnout": "close", + "zavrit": "close", "zhasnout": "turn_off", + "zmenit": "set", "ztlumit": "dim", + "zvysit": "increase", + # local-only + "v": "v", "y_e": "y_e", + }, + # Chinese (Simplified) -------------------------------------------- + "zh-CN": { + "add_to": "add", "set_to": "set", + "how_many_is": "how_many", "how_much": "how_much", + }, + # Chinese (Hong Kong) --------------------------------------------- + "zh-HK": { + "set_to": "set", "how_many_is": "how_many", + "how": "how", + }, + # Chinese (Traditional) ------------------------------------------- + "zh-TW": { + "set_to": "set", "how_many_is": "how_many", + }, + # Swedish --------------------------------------------------------- + "sv": { + "alla": "all", "andra": "set", + "ar": "is", "batteri": "battery", + "dimra": "dim", "farga": "color", + "gardiner": "curtains", + "hemma": "home", "inga": "none", "i_pa": "in_on", + "kall": "cold", "lasbar": "lockable", + "ljusintensitet": "brightness", + "ljuskallor": "light_sources", + "maximal": "max", "minimal": "min", + "mojliga": "possible", + "oppna_gardiner": "open_curtains", + "procent": "percent", + "satt_numeriskt_varde": "numeric_value_set", + "sla_av": "turn_off", "sla_pa": "turn_on", + "stang_gardiner": "close_curtains", + "time_left": "time_left", + "vad": "what", "var": "where", "varm": "warm", + "varmt_kallt": "warm_cold", "vilka": "which", + }, + # Slovenian ------------------------------------------------------- + "sl": { + "dodaj": "add", + "izkljuci": "turn_off", "izklopi": "turn_off", + "ugasni": "turn_off", + "vkljuci": "turn_on", "vklopi": "turn_on", + "prizgi": "turn_on", + "odpri": "open", "zapri": "close", + "kaksna_je": "what_is", "kaksno_je": "what_is", + "katera": "which", "katera_je": "which", + "kateri": "which", "kje_je": "where_is", + "luc": "light", + "pol": "half", "povsod": "everywhere", + "so_vsi": "are_all", "spremeni": "set", + "stopinj": "degrees", "ventilator": "fan", + "vsa": "all", "vse": "all", "vsi": "all", + # local-only + "v": "v", + }, + # Welsh ----------------------------------------------------------- + "cy": { + "agor": "open", "cau": "close", + "cynnau": "turn_on", "diffodd": "turn_off", + "amser_canslo": "timer_cancel", "amser_gosod": "timer_set", + "ar_hyn_o_bryd": "currently", + "beth_yw": "what_is", "ble_mae": "where_is", + "cartref": "home", "cyflwr": "state", + "faint": "how_many", "glanhau": "clean", + "golau": "light", "gosod": "set", + "gosod_rhif": "numeric_value_set", + "oes": "is", "oes_unrhyw": "is_any", + "pa": "which", "pob": "all", + "rhoi": "set", "sut_mae": "how_is", + "troi": "turn", "tymheredd_gair": "temperature_word", + "unrhyw": "any", "yma": "here", + "ym_mhobman": "everywhere", "ydy_pob": "is_all", + # local-only + "ar": "ar", "ein": "ein", "fy": "fy", + "i": "i", "o": "o", "y": "y", "yn": "yn", + }, + # Polish ---------------------------------------------------------- + "pl": { + "turn_on_light": "turn_on", + }, + # Vietnamese ------------------------------------------------------ + "vi": { + "mo": "open", "bao_nhieu": "how_many", + "every_where": "everywhere", "in_here": "here", + "showed_by": "shown_by", + # local-only + "ong": "ong", "of": "of", "in": "in", + }, + # Thai ------------------------------------------------------------ + "th": { + "be": "is", "is_it": "is_it", + "lead_in": "lead_in", "now": "now", + "second_person": "you", + "temperature_unit": "temperature_unit", + "timer_status_query": "timer_status", "timer_word": "timer", + # local-only Thai politeness markers + "polite_prefix": "polite_prefix", + "polite_prefix_base": "polite_prefix_base", + "polite_suffix": "polite_suffix", + "polite_suffix_core": "polite_suffix_core", + "polite_suffix_ending": "polite_suffix_ending", + }, + # Croatian / Serbian-Latin (very similar) ------------------------- + "hr": { + "iskljuci": "turn_off", "ukljuci": "turn_on", + "otvori": "open", "zatvori": "close", + "kakvo_je": "what_is", "koja_je": "which", + "sve": "all", "promijeni": "set", + "stupanj": "degree", "prognoza": "forecast", + }, + "sr-Latn": { + "iskljuci": "turn_off", "ukljuci": "turn_on", + "otvori": "open", "zatvori": "close", + "kakvo_je": "what_is", "koja_je": "which", + "sve": "all", "promeni": "set", + "stepen": "degree", "prognoza": "forecast", + }, + # Portuguese (generic — also covers `pt`) ------------------------- + "pt": { + "abrir": "open", "fechar": "close", + "ligar": "turn_on", "desligar": "turn_off", + "adicionar": "add", "colocar": "put", + "juntar": "add", "mudar": "set", + "esta": "is", "estores": "blinds", + "algum": "some", "qual": "which", "luz": "light", + "temporizador": "timer", "temporizadores": "timers", + "temporizador_cancelar": "timer_cancel", + # local-only + "por": "por", + }, + # Luxembourgish --------------------------------------------------- + "lb": { + "maach": "do", "op": "on", "zou": "off", + "all_window": "all_windows", + }, + # Swiss German ---------------------------------------------------- + "de-CH": { + "ab_us": "turn_off", "a_y": "turn_on", + "gerate": "device", "liecht": "light", + "liechter": "lights", "mach": "do", "steu": "control", + }, + # Arabic — slugs already English-shaped, just a few stragglers ---- + "ar": { + "procent": "percent", "go": "go", "go_back": "go_back", + "remains": "remaining", "this": "this", + "home_assistant": "home_assistant", + }, + # Danish ---------------------------------------------------------- + "da": { + # actions + "tnd": "turn_on", "sluk": "turn_off", + "luk": "close", "abn": "open", + "dkke_abn": "open_cover", "dkke_luk": "close_cover", + "dkke_ned": "lower_cover", "dkke_op": "raise_cover", + "afspil": "play", "afspiller": "play", + "spring": "skip", "spring_til": "skip_to", + "broadcast": "broadcast", "start": "start", "stop": "stop", + "rengr": "clean", "skabte": "create", + "fuldfr": "complete", "fuldfrt": "completed", + "genoptag": "resume", "opfanger": "detect", + "med_navnet": "named", "pa_alle_lys": "on_all_lights", + "kommandoer": "commands", "altid": "always", + # concepts + "nu": "now", "her": "here", "totalt": "all", + "forrige": "previous", "nogen": "any", + "er_nogen": "is_any", "er_noget": "is_anything", + "hvilke": "which", "hvor_mange": "how_many", + "den": "the_one", "denne": "this", + "aktuel_tid": "current_time", "aktuelle": "current", + "dags_dato": "today_date", "dato": "date", + "alle_destinationer": "all_destinations", + "lys": "light", "lydstyrke": "volume", + "farve": "color", "koldt": "cold", + "koldt_varmt": "cold_warm", "temperaturen": "temperature", + "carbonmonoxid": "carbon_monoxide", + "carbonmonoxid_sensor": "carbon_monoxide_sensor", + "gas_fundet": "gas_detected", + "gas_ikke_fundet": "gas_not_detected", + "bevaegelses_sensor": "motion_sensor", + "blser": "blinds", "garagedr": "garage_door", + "gardin": "curtain", "indkbsliste": "shopping_list", + "lasbar": "lockable", "nedbr": "rainfall", + "persienne": "shutter", "rullegardin": "roller_blind", + "skodde": "shutter_cover", "sang": "song", + "script": "script", "status": "state", + "medie": "media", "enhed": "device", + "koncentration": "concentration", "procent": "percent", + "st_numerisk_vrdi": "numeric_value_set", + "sprg_om_destination": "ask_for_destination", + "sprg_om_vrdi": "ask_for_value", + "i_pa": "in_on", "i": "in", + "timer_add": "timer_add", "timer_decrease": "timer_decrease", + "timer_pause": "timer_pause", "timer_unpause": "timer_unpause", + "timer": "timer", "timers": "timers", + "hilsen": "greeting", "mine_data": "my_data", + "abn": "open", "al": "all", + }, +} + +# --------------------------------------------------------------------------- +# Common area names — seeded per language so the `{area}` slot gets a +# head-start even though hassil treats area as a wildcard. This is a +# best-effort list; contributors can extend it for missing languages. +# --------------------------------------------------------------------------- + +COMMON_AREA_NAMES: dict[str, list[str]] = { + "en": ["kitchen", "living room", "bedroom", "bathroom", "office", "garage", "hallway", "basement", "dining room", "garden", "terrace", "laundry room"], + "de": ["Küche", "Wohnzimmer", "Schlafzimmer", "Badezimmer", "Büro", "Garage", "Flur", "Keller", "Esszimmer", "Garten", "Terrasse", "Waschküche"], + "fr": ["cuisine", "salon", "chambre", "salle de bain", "bureau", "garage", "couloir", "sous-sol", "salle à manger", "jardin", "terrasse", "buanderie"], + "es": ["cocina", "salón", "dormitorio", "baño", "oficina", "garaje", "pasillo", "sótano", "comedor", "jardín", "terraza", "lavandería"], + "pt": ["cozinha", "sala de estar", "quarto", "casa de banho", "escritório", "garagem", "corredor", "cave", "sala de jantar", "jardim", "terraço", "lavandaria"], + "pt-BR": ["cozinha", "sala", "quarto", "banheiro", "escritório", "garagem", "corredor", "porão", "sala de jantar", "jardim", "terraço", "lavanderia"], + "it": ["cucina", "soggiorno", "camera da letto", "bagno", "ufficio", "garage", "corridoio", "seminterrato", "sala da pranzo", "giardino", "terrazza", "lavanderia"], + "nl": ["keuken", "woonkamer", "slaapkamer", "badkamer", "kantoor", "garage", "gang", "kelder", "eetkamer", "tuin", "terras", "wasruimte"], + "ca": ["cuina", "sala d'estar", "dormitori", "bany", "oficina", "garatge", "passadís", "soterrani", "sala de dinar", "jardí", "terrassa", "sala de bugada"], + "da": ["køkken", "stue", "soveværelse", "badeværelse", "kontor", "garage", "gang", "kælder", "spisestue", "have", "terrasse", "vaskerum"], + "sv": ["kök", "vardagsrum", "sovrum", "badrum", "kontor", "garage", "hall", "källare", "matsal", "trädgård", "terrass", "tvättstuga"], + "nb": ["kjøkken", "stue", "soverom", "bad", "kontor", "garasje", "gang", "kjeller", "spisestue", "hage", "terrasse", "vaskerom"], + "fi": ["keittiö", "olohuone", "makuuhuone", "kylpyhuone", "toimisto", "autotalli", "käytävä", "kellari", "ruokailuhuone", "puutarha", "terassi", "pesula"], + "pl": ["kuchnia", "salon", "sypialnia", "łazienka", "biuro", "garaż", "korytarz", "piwnica", "jadalnia", "ogród", "taras", "pralnia"], + "ru": ["кухня", "гостиная", "спальня", "ванная", "офис", "гараж", "коридор", "подвал", "столовая", "сад", "терраса", "прачечная"], + "ro": ["bucătărie", "sufragerie", "dormitor", "baie", "birou", "garaj", "hol", "subsol", "sală de mese", "grădină", "terasă", "spălătorie"], + "ar": ["مطبخ", "غرفة المعيشة", "غرفة النوم", "حمام", "مكتب", "مرآب", "ممر", "قبو", "غرفة الطعام", "حديقة", "شرفة", "غرفة الغسيل"], + "he": ["מטבח", "סלון", "חדר שינה", "אמבטיה", "משרד", "מוסך", "מסדרון", "מרתף", "חדר אוכל", "גן", "מרפסת", "חדר כביסה"], + "ja": ["キッチン", "リビング", "寝室", "浴室", "書斎", "ガレージ", "廊下", "地下室", "ダイニング", "庭", "テラス", "洗濯室"], + "ko": ["부엌", "거실", "침실", "욕실", "사무실", "차고", "복도", "지하실", "식당", "정원", "테라스", "세탁실"], + "zh-CN": ["厨房", "客厅", "卧室", "浴室", "办公室", "车库", "走廊", "地下室", "餐厅", "花园", "露台", "洗衣房"], + "zh-TW": ["廚房", "客廳", "臥室", "浴室", "辦公室", "車庫", "走廊", "地下室", "餐廳", "花園", "露台", "洗衣房"], + "zh-HK": ["廚房", "客廳", "臥室", "浴室", "辦公室", "車庫", "走廊", "地下室", "餐廳", "花園", "露台", "洗衣房"], + "el": ["κουζίνα", "σαλόνι", "υπνοδωμάτιο", "μπάνιο", "γραφείο", "γκαράζ", "διάδρομος", "υπόγειο", "τραπεζαρία", "κήπος", "βεράντα", "πλυντήριο"], + "hu": ["konyha", "nappali", "hálószoba", "fürdőszoba", "iroda", "garázs", "folyosó", "pince", "étkező", "kert", "terasz", "mosókonyha"], + "cs": ["kuchyně", "obývací pokoj", "ložnice", "koupelna", "kancelář", "garáž", "chodba", "sklep", "jídelna", "zahrada", "terasa", "prádelna"], + "sk": ["kuchyňa", "obývačka", "spálňa", "kúpeľňa", "kancelária", "garáž", "chodba", "pivnica", "jedáleň", "záhrada", "terasa", "práčovňa"], + "sl": ["kuhinja", "dnevna soba", "spalnica", "kopalnica", "pisarna", "garaža", "hodnik", "klet", "jedilnica", "vrt", "terasa", "pralnica"], + "hr": ["kuhinja", "dnevni boravak", "spavaća soba", "kupaonica", "ured", "garaža", "hodnik", "podrum", "blagovaonica", "vrt", "terasa", "praona"], + "sr": ["кухиња", "дневна соба", "спаваћа соба", "купатило", "канцеларија", "гаража", "ходник", "подрум", "трпезарија", "башта", "тераса", "пераоница"], + "sr-Latn": ["kuhinja", "dnevna soba", "spavaća soba", "kupatilo", "kancelarija", "garaža", "hodnik", "podrum", "trpezarija", "bašta", "terasa", "peraonica"], + "bg": ["кухня", "хол", "спалня", "баня", "офис", "гараж", "коридор", "мазе", "трапезария", "градина", "тераса", "пералня"], + "uk": ["кухня", "вітальня", "спальня", "ванна кімната", "офіс", "гараж", "коридор", "підвал", "їдальня", "сад", "тераса", "пральня"], + "tr": ["mutfak", "oturma odası", "yatak odası", "banyo", "ofis", "garaj", "koridor", "bodrum", "yemek odası", "bahçe", "teras", "çamaşır odası"], + "th": ["ครัว", "ห้องนั่งเล่น", "ห้องนอน", "ห้องน้ำ", "ห้องทำงาน", "โรงรถ", "ทางเดิน", "ใต้ดิน", "ห้องอาหาร", "สวน", "ระเบียง", "ห้องซักรีด"], + "vi": ["nhà bếp", "phòng khách", "phòng ngủ", "phòng tắm", "văn phòng", "ga-ra", "hành lang", "tầng hầm", "phòng ăn", "vườn", "hiên", "phòng giặt"], + "id": ["dapur", "ruang tamu", "kamar tidur", "kamar mandi", "kantor", "garasi", "koridor", "ruang bawah tanah", "ruang makan", "taman", "teras", "ruang cuci"], + "ms": ["dapur", "ruang tamu", "bilik tidur", "bilik air", "pejabat", "garaj", "koridor", "ruang bawah tanah", "ruang makan", "taman", "teres", "bilik dobi"], + "ga": ["cistin", "seomra suí", "seomra codlata", "seomra folctha", "oifig", "garáiste", "halla", "bunús", "seomra bia", "gairdín", "terrás", "seomra níocháin"], + "cy": ["gegin", "ystafell fyw", "ystafell wely", "ystafell ymolchi", "swyddfa", "garej", "coridor", "celar", "ystafell fwyta", "gardd", "teras", "ystafell golchi"], + "et": ["köök", "elutuba", "magamistuba", "vannituba", "kontor", "garaaž", "koridor", "kelder", "söögituba", "aed", "terass", "pesuruum"], + "lt": ["virtuvė", "svetainė", "miegamasis", "vonios kambarys", "biuras", "garažas", "koridorius", "rūsys", "valgomasis", "sodas", "terasa", "skalbykla"], + "lv": ["virtuve", "dzīvojamā istaba", "guļamistaba", "vannas istaba", "birojs", "garāža", "koridors", "pagrabs", "ēdamistaba", "dārzs", "terase", "veļas mazgātava"], + "is": ["eldhús", "stofa", "svefnherbergi", "baðherbergi", "skrifstofa", "bílskúr", "gangur", "kjallari", "borðstofa", "garður", "svalir", "þvottahús"], + "af": ["kombuis", "sitkamer", "slaapkamer", "badkamer", "kantoor", "motorhuis", "gang", "kelder", "eetkamer", "tuin", "terras", "wasgoedkamer"], + "sw": ["jiko", "sebule", "chumba cha kulala", "bafu", "ofisi", "jengo la magari", "njia", "chumba cha chini", "chumba cha kula", "bustani", "varanda", "chumba cha kufulia"], + "eu": ["sukaldea", "egongela", "logela", "bainugela", "bulegoa", "garajea", "korridorea", "sotoa", "janogela", "lorategia", "terraza", "garbitze-gela"], + "gl": ["cociña", "sala de estar", "dormitorio", "cuarto de baño", "oficina", "garaxe", "corredor", "sótano", "comedor", "xardín", "terraza", "lavandería"], + "fa": ["آشپزخانه", "اتاق نشیمن", "اتاق خواب", "حمام", "دفتر", "گاراژ", "راهرو", "زیرزمین", "اتاق غذاخوری", "باغ", "تراس", "اتاق لباسشویی"], + "ne": ["भान्सा", "बैठक कोठा", "सुत्ने कोठा", "बाथरूम", "कार्यालय", "ग्यारेज", "बार", "भुइँतला", "भोजन कोठा", "बगैंचा", "चर्पी", "धुन पखाल्ने कोठा"], + "ka": ["სამზარეულო", "მისაღები ოთახი", "საძინებელი", "აბაზანა", "ოფისი", "ავტოფარეხი", "კორიდორი", "სარდაფი", "სასადილო ოთახი", "ფანჯარა", "ტერასა", "საპარსი"], + "bn": ["রান্নাঘর", "বসার ঘর", "শোনার ঘর", "বাথরুম", "অফিস", "গ্যারেজ", "বারান্দা", "বেসমেন্ট", "খাবার ঘর", "বাগান", "টেরাস", "ধোপার ঘর"], + "gu": ["રસોડું", "બેઠક", "સુવાનો ઓરડો", "બાથરૂમ", "ઓફિસ", "ગેરેજ", "બાર", "બેઝમેન્ટ", "જમવાનું ઓરડો", "બગીચો", "ટેરેસ", "ધોવાનો ઓરડો"], + "hi": ["रसोई", "बैठक", "बेडरूम", "बाथरूम", "दफ़्तर", "गैराज", "गली", "बेसमेंट", "भोजन कक्ष", "बगीचा", "बरामदा", "धोने का कमरा"], + "kn": ["ಅಡಿಗೆ ಮನೆ", "ಹಾಲ್", "ನಿದ್ರಾಕೋಶ", "ಬಾತ್ರೂಮ್", "ಕಚೇರಿ", "ಗಾರೇಜ್", "ಕಾರಿಡಾರ್", "ಬೇಸ್ಮೆಂಟ್", "ಊಟದ ಕೋಶ", "ತೋಟ", "ಟೆರೇಸ್", "ಒಗ್ಗುವ ಕೋಶ"], + "ml": ["അടുക്കളം", "കഴിക്കുന്ന മുറി", "കിടക്കയ്ക്കുള്ള മുറി", "കുളിമുറി", "ഓഫീസ്", "ഗാരേജ്", "കോറിഡോർ", "ബേസ്മെന്റ്", "ഭക്ഷണമുറി", "പൂന്തോട്ടം", "ടെറസ്", "വസ്ത്രം കഴിയുന്ന മുറി"], + "mr": ["स्वयंपाकघर", "बसण्याची खोली", "जोपायची खोली", "बाथरूम", "ऑफिस", "गॅरेज", "कॉरिडॉर", "बेसमेंट", "जेवणाची खोली", "बाग", "टेरेस", "धुण्याची खोली"], + "pa": ["ਰਸੋਈ", "ਬੈਠਕ", "ਸੌਣ ਵਾਲਾ ਕਮਰਾ", "ਬਾਥਰੂਮ", "ਦਫਤਰ", "ਗੈਰੇਜ", "ਗਲੀ", "ਬੇਸਮੈਂਟ", "ਖਾਣ ਵਾਲਾ ਕਮਰਾ", "ਬਾਗ", "ਟੈਰੇਸ", "ਧੋਣ ਵਾਲਾ ਕਮਰਾ"], + "ta": ["சமையலறை", "பெற்று அறை", "கடுக்கை அறை", "குளியலறை", "பணி இடம்", "வாகன நிறுத்தம்", "நடைபாதை", "அடித்தளம்", "உணவு அறை", "தோட்டம்", "முற்றம்", "துவைப்பு அறை"], + "te": ["వంటగది", "సభా గది", "నిద్ర గది", "స్నానగది", "కార్యాలయం", "గ్యారేజ్", "కారిడార్", "బేస్మెంట్", "భోజన గది", "తోట", "టెరస్", "ఉతికే గది"], + "ur": ["باتھ روم", "بیٹھک", "سونے کا کمرہ", "باتھ روم", "دفتر", "گیراج", "گلی", "تہہ خانہ", "کھانے کا کمرہ", "باغ", "چبوترا", "دھونے کا کمرہ"], + "mn": ["гал тогоо", "зочны өрөө", "унтлагын өрөө", "усанд орох өрөө", "оффис", "гараж", "коридор", "суурь", "хоолны өрөө", "цэцэрлэг", "терасс", "угаалгын өрөө"], + "kw": ["kek", "rom godhesi", "kewor", "rom ymolchi", "offis", "garaj", "koryor", "kelder", "rom dybri", "lowarth", "teras", "rom yowghi"], + "lb": ["Kichen", "Wunnzëmmer", "Schlofzëmmer", "Buedzëmmer", "Büro", "Garage", "Gank", "Keller", "Iesszëmmer", "Gaart", "Terrass", "Wäschkichen"], +} + +# --------------------------------------------------------------------------- +# Template rewriting +# --------------------------------------------------------------------------- + +_SLOT_RE = re.compile(r"\{([^{}]+)\}") +_RULE_RE = re.compile(r"<([^<>\s]+)>") +_PERM_RE = re.compile(r"\(([^()]*;[^()]*)\)") # innermost permutation +_JINJA_SET_RE = re.compile(r"\{%\s*set\b.*?%\}", re.DOTALL) +_JINJA_IF_RE = re.compile(r"\{%\s*if\b[^%]*%\}", re.DOTALL) +_JINJA_ENDIF_RE = re.compile(r"\{%\s*endif\s*%\}", re.DOTALL) +_JINJA_BRANCH_RE = re.compile(r"\{%\s*el(?:if|se)\b[^%]*%\}", re.DOTALL) +_JINJA_SLOT_DOT_RE = re.compile( + r"\{\{\s*(?:slots|state|query)\.([a-zA-Z_][a-zA-Z0-9_]*)\s*(?:\|[^}]+?)?\s*\}\}" +) +_JINJA_VAR_RE = re.compile(r"\{\{\s*([a-zA-Z_][a-zA-Z0-9_]*)\s*(?:\|[^}]+?)?\s*\}\}") +_JINJA_LEFTOVER_RE = re.compile(r"\{\{|\}\}|\{%|%\}") + + +def _rewrite_slot(body: str, slot_to_list: dict[str, str]) -> str: + body = body.strip() + if body.startswith("@"): + name = _slug(body[1:].strip()) + slot_to_list.setdefault(name, name) + return "{" + name + "}" + if ":" in body: + list_name, slot = (p.strip() for p in body.split(":", 1)) + list_name = _slug(list_name) + slot = _slug(slot.lstrip("@").strip()) + slot_to_list.setdefault(slot, list_name) + return "{" + slot + "}" + name = _slug(body) + slot_to_list.setdefault(name, name) + return "{" + name + "}" + + +def _rewrite_slots(s: str, slot_to_list: dict[str, str]) -> str: + return _SLOT_RE.sub(lambda m: _rewrite_slot(m.group(1), slot_to_list), s) + + +def _expand_rules(s: str, rules: dict[str, str], max_depth: int = 12) -> str: + for _ in range(max_depth): + def _sub(m: re.Match[str]) -> str: + name = _slug(m.group(1)) + if name in rules: + return "(" + rules[name] + ")" + return f"<{name}>" # rewrite the reference to its slugged form + new = _RULE_RE.sub(_sub, s) + if new == s or len(new) > MAX_RULE_BYTES: + return new + s = new + return s + + +def _expand_perms(s: str) -> str: + while True: + m = _PERM_RE.search(s) + if not m: + return s + parts = [p.strip() for p in m.group(1).split(";") if p.strip()] + if len(parts) < 2: + replacement = " ".join(parts) + elif len(parts) > MAX_PERM_ELEMS: + # bail out — n! is too big to enumerate safely + replacement = " ".join(parts) + else: + orderings = [" ".join(p) for p in itertools.permutations(parts)] + replacement = "(" + "|".join(orderings) + ")" + s = s[: m.start()] + replacement + s[m.end():] + + +def _collapse_single_branch(s: str) -> str: + """`(X)` with no top-level `|` → `X`. Balance-aware so the check is + correct for nested groups. Subsumes `((Y))` → `(Y)` cleanup.""" + changed = True + while changed: + changed = False + i = 0 + while i < len(s): + if s[i] != "(": + i += 1 + continue + depth, bdepth, has_top_pipe, end = 0, 0, False, -1 + for j in range(i, len(s)): + ch = s[j] + if ch == "(": + depth += 1 + elif ch == ")": + depth -= 1 + if depth == 0: + end = j + break + elif ch == "[": + bdepth += 1 + elif ch == "]": + bdepth -= 1 + elif ch == "|" and depth == 1 and bdepth == 0: + has_top_pipe = True + if end < 0: + break + if not has_top_pipe: + s = s[:i] + s[i + 1 : end] + s[end + 1 :] + changed = True + continue + i += 1 + return s + + +def _normalise_whitespace(s: str) -> str: + return re.sub(r"\s+", " ", s).strip() + + +# --------------------------------------------------------------------------- +# Literal enumeration — used to promote heavy rules into entity value sets +# --------------------------------------------------------------------------- + + +def _match_close(s: str, start: int, opener: str, closer: str, end: int) -> int: + depth = 0 + for j in range(start, end): + if s[j] == opener: + depth += 1 + elif s[j] == closer: + depth -= 1 + if depth == 0: + return j + return -1 + + +def _enum_seq(s: str, start: int, end: int, cap: int) -> list[str]: + """Cartesian-enumerate the literal strings produced by `s[start:end]`.""" + result = [""] + i = start + while i < end: + ch = s[i] + if ch == "(": + j = _match_close(s, i, "(", ")", end) + if j < 0: + break + branches = _enum_alt(s, i + 1, j, cap) + result = [a + b for a in result for b in branches] + if len(result) > cap: + raise OverflowError("enumeration cap exceeded") + i = j + 1 + elif ch == "[": + j = _match_close(s, i, "[", "]", end) + if j < 0: + break + branches = [""] + _enum_alt(s, i + 1, j, cap) + result = [a + b for a in result for b in branches] + if len(result) > cap: + raise OverflowError("enumeration cap exceeded") + i = j + 1 + else: + result = [a + ch for a in result] + i += 1 + return result + + +def _enum_alt(s: str, start: int, end: int, cap: int) -> list[str]: + """Split `s[start:end]` on top-level `|`, enumerate each branch.""" + branches: list[str] = [] + depth_p = depth_b = 0 + bs = start + for i in range(start, end): + ch = s[i] + if ch == "(": + depth_p += 1 + elif ch == ")": + depth_p -= 1 + elif ch == "[": + depth_b += 1 + elif ch == "]": + depth_b -= 1 + elif ch == "|" and depth_p == 0 and depth_b == 0: + branches.extend(_enum_seq(s, bs, i, cap)) + if len(branches) > cap: + raise OverflowError("enumeration cap exceeded") + bs = i + 1 + branches.extend(_enum_seq(s, bs, end, cap)) + return branches + + +def _slot_structure(s: str) -> str: + """Strip literal text from a template, keeping only structural chars + and `{slot}` markers — used by the adjacency / repeated-slot checks + so they can see through nested optionals.""" + out: list[str] = [] + in_slot = False + for ch in s: + if in_slot: + out.append(ch) + if ch == "}": + in_slot = False + elif ch == "{": + in_slot = True + out.append(ch) + elif ch in "()[]|": + out.append(ch) + else: + out.append(" ") + return "".join(out) + + +def _enumerate(template: str, cap: int) -> list[str]: + """Materialise the literal value set of a slot-free template. Returns + a sorted, deduped, whitespace-normalised list. Raises ``OverflowError`` + if the Cartesian product exceeds ``cap``. Wraps the input in a + virtual outer alternation group so that top-level `|` (the hassil + convention for verb tables) is treated as branch-splitting rather + than a literal pipe character.""" + raw = _enum_alt(template, 0, len(template), cap) + seen: dict[str, None] = {} + for v in raw: + v = _normalise_whitespace(v) + if v: + seen.setdefault(v, None) + return sorted(seen) + + +def _ascii_fold(s: str) -> str: + """Strip combining marks (á → a) and drop any remaining non-ASCII.""" + return "".join( + c for c in unicodedata.normalize("NFKD", s) + if ord(c) < 128 and not unicodedata.combining(c) + ) + + +def _slug(name: str) -> str: + """Sanitise an identifier so OVOS-INTENT-2's `[a-z0-9_]` charset + accepts it. Accented Latin letters fold to their base ASCII form; + non-Latin scripts that fold to empty fall back to a stable + hex-hash slug derived from the original name.""" + folded = _ascii_fold(name).lower() + s = re.sub(r"[^a-z0-9_]+", "_", folded) + s = re.sub(r"_+", "_", s).strip("_") + if not s: + s = "x_" + hashlib.sha1(name.encode("utf-8")).hexdigest()[:8] + elif s[0].isdigit(): + s = "x_" + s + return s + + +def _snake_case(name: str) -> str: + s = re.sub(r"(.)([A-Z][a-z]+)", r"\1_\2", name) + s = re.sub(r"([a-z0-9])([A-Z])", r"\1_\2", s) + return _slug(s) + + +def _path_violation(path: str) -> str | None: + slots = re.findall(r"\{([^{}]+)\}", path) + if len(slots) != len(set(slots)): + return "repeated_slot (OVOS-INTENT-1 §3.6 forbids)" + if re.search(r"\}\s*\{", path): + return "adjacent_slots (OVOS-INTENT-1 §3.6 forbids)" + return None + + +def _voc_looks_garbage(values: list[str]) -> bool: + """Detect auto-promoted rules that produced meaningless particle stacks + (e.g. 500 lines of the preposition 'на' repeated 1-4 times). + + Two signals are used: + 1. Ratio of values to unique words is very high (Cartesian-product + explosion of a small word pool). + 2. A single short particle dominates the tokens and many values are + just that particle repeated. + """ + from collections import Counter + words = [w for v in values for w in v.split()] + if not words: + return False + c = Counter(words) + # Signal 1: combinatorial blow-up of a tiny word pool + if len(values) > 10 * len(c): + return True + # Signal 2: particle stacking + most_common_word, most_common_count = c.most_common(1)[0] + if len(most_common_word) > 4: + return False + if most_common_count / len(words) > 0.5: + pure_repeats = sum( + 1 for v in values + if set(v.split()) == {most_common_word} + ) + if pure_repeats / len(values) > 0.2: + return True + return False + + +def rewrite_template( + s: str, rules: dict[str, str], slot_to_list: dict[str, str] +) -> tuple[list[str] | None, str | None]: + """Returns ``(lines, reason)``. ``lines`` is the list of output samples + — usually one entry (the compact rewritten template), but when the + template has *some* enumerated paths that violate OVOS-INTENT-1 §3.6 + we fall back to materialising the *valid* paths individually so the + sample still contributes coverage instead of being dropped wholesale.""" + s = _expand_rules(s, rules) + if len(s) > MAX_SAMPLE_BYTES: + return None, f"sample_too_large_after_rule_inline ({len(s)}B > {MAX_SAMPLE_BYTES})" + s = _expand_perms(s) + if len(s) > MAX_SAMPLE_BYTES: + return None, f"sample_too_large_after_perm_expansion ({len(s)}B > {MAX_SAMPLE_BYTES})" + s = _rewrite_slots(s, slot_to_list) + s = _collapse_single_branch(s) + paths = _cartesian_paths(s) + if paths > MAX_SAMPLE_PATHS: + return None, f"cartesian_explosion ({paths} paths > {MAX_SAMPLE_PATHS})" + + # Check the slot-only structure: any enumerated path with adjacent or + # repeated slots is invalid under OVOS-INTENT-1 §3.6. + try: + struct_paths = _enumerate(_slot_structure(s), MAX_SAMPLE_PATHS) + except OverflowError: + struct_paths = [] + bad_path = next((p for p in struct_paths if _path_violation(p)), None) + + if not bad_path: + result = _normalise_whitespace(s) + if not result: + return None, "empty_after_rewrite" + # The compact form itself can violate §3.6 when the same slot + # appears in multiple branches of an alternation (e.g. fi's + # `({area}|{area})`). Force salvage in that case. + if _path_violation(result): + bad_path = result # type: ignore[assignment] + else: + return [result], None + + # At least one enumerated path is invalid. Materialise the *full* + # template (literal text included) and keep only the paths that pass + # validation. The compact form is sacrificed, but the sample still + # contributes the recoverable subset to the intent corpus. + try: + full = _enumerate(s, MAX_SAMPLE_PATHS) + except OverflowError: + return None, "enumeration_overflow" + by_sig: dict[frozenset[str], list[str]] = {} + seen: set[str] = set() + for e in full: + norm = _normalise_whitespace(e) + if not norm or norm in seen or _path_violation(norm): + continue + seen.add(norm) + sig = frozenset(re.findall(r"\{([^{}]+)\}", norm)) + by_sig.setdefault(sig, []).append(norm) + if not by_sig: + return None, "all_paths_invalid (every enumeration violates §3.6)" + # With OVOS-INTENT-1 v3, `.intent` files allow templates with + # different slot sets (union semantics). Keep every valid path. + all_valid: list[str] = [] + seen_out: set[str] = set() + for lines in by_sig.values(): + for line in lines: + if line not in seen_out: + seen_out.add(line) + all_valid.append(line) + return all_valid, None + + +def _cartesian_paths(s: str) -> int: + """Rough upper bound on enumerated sample count: at each group `(a|b)` + multiply by (1 + pipe-count); at each `[x]` multiply by 2. Hassil + rules often use top-level alternation (`A|B|C` with no enclosing + parens) — we wrap in a virtual outer group so those branches are + counted correctly.""" + paths = 1 + depth_stack: list[int] = [1] # implicit outer group for top-level `|` + for ch in s: + if ch == "(": + depth_stack.append(1) + elif ch == ")": + if len(depth_stack) > 1: + paths *= depth_stack.pop() + if paths > 10 ** 9: + return paths + elif ch == "|": + depth_stack[-1] += 1 + elif ch == "[": + paths *= 2 + if paths > 10 ** 9: + return paths + # Apply the virtual outer group's branch count. + paths *= depth_stack[0] + return paths + + +# --------------------------------------------------------------------------- +# Lists / responses +# --------------------------------------------------------------------------- + + +def _list_values(body: dict) -> list[str] | None: + if not isinstance(body, dict) or body.get("wildcard"): + return None + if body.get("values"): + flat: list[str] = [] + for v in body["values"]: + if isinstance(v, str): + flat.append(v) + elif isinstance(v, dict) and "in" in v: + inp = v["in"] + flat.extend(inp if isinstance(inp, list) else [inp]) + # Hassil list values are themselves templates — they can carry + # `(...)` groups around single morphological variants that the + # OVOS lint treats as malformed single-branch groups. Normalise + # each value through the same collapse pass we use on samples. + return [ + _normalise_whitespace(_collapse_single_branch(s)) or s + for s in flat + ] or None + rng = body.get("range") + if isinstance(rng, dict) and "from" in rng and "to" in rng: + step = int(rng.get("step", 1) or 1) + lo, hi = int(rng["from"]), int(rng["to"]) + if (hi - lo) // max(step, 1) + 1 > MAX_ENTITY_VALUES: + return None + return [str(n) for n in range(lo, hi + 1, step)] + return None + + +def _split_jinja_if(s: str) -> list[str]: + """Decompose `{% if … %} A {% elif … %} B {% else %} C {% endif %}` + into branch bodies [A, B, C]. Each branch becomes a separate dialog + phrase — the renderer picks one at random, which is a reasonable + coarse approximation of the original conditional. Non-conditional + text outside the if-block is prepended to each branch.""" + m_if = _JINJA_IF_RE.search(s) + if not m_if: + return [s] + m_endif = _JINJA_ENDIF_RE.search(s, m_if.end()) + if not m_endif: + return [s] + prefix = s[: m_if.start()] + suffix = s[m_endif.end() :] + inside = s[m_if.end() : m_endif.start()] + branches = _JINJA_BRANCH_RE.split(inside) + return [prefix + b + suffix for b in branches if b.strip()] + + +def _normalise_response(s: str) -> tuple[list[str] | None, str | None]: + """Returns ``(lines, reason)``. A single hassil response may decompose + into multiple OVOS dialog phrases (one per Jinja if/elif/else branch).""" + s = _JINJA_SET_RE.sub("", s) + out: list[str] = [] + for branch in _split_jinja_if(s): + # Recurse once for nested if-blocks (rare in hassil). + sub_branches = _split_jinja_if(branch) if "{% if" in branch[len(branch) - len(branch.lstrip()):] else [branch] + for b in sub_branches: + b = _JINJA_SLOT_DOT_RE.sub(lambda m: "{" + m.group(1) + "}", b) + b = _JINJA_VAR_RE.sub(lambda m: "{" + m.group(1) + "}", b) + if _JINJA_LEFTOVER_RE.search(b): + continue + norm = _normalise_whitespace(b) + if not norm or re.search(r"\}\s*\{", norm): + continue + slots = re.findall(r"\{([^{}]+)\}", norm) + if len(slots) != len(set(slots)): + continue + # OVOS forbids slot-only templates — a dialog phrase must + # carry at least one literal word. Drop branches that lost + # all their literal text after Jinja stripping. + if not re.sub(r"\{[^{}]+\}", "", norm).strip(): + continue + out.append(norm) + if not out: + return None, "jinja_template_unresolvable" + # Return all valid branches — the driver groups them by slot + # signature and emits one .dialog file per group, so each Jinja + # if/elif/else branch survives instead of being collapsed. + return out, None + + +# --------------------------------------------------------------------------- +# Streaming I/O +# --------------------------------------------------------------------------- + + +class _StreamWriter: + """Append unique non-empty lines to a file. Holds only the dedupe set + for the currently-open file — never buffers a whole corpus.""" + + def __init__(self, path: Path): + self.path = path + self.seen: set[str] = set() + self.fh = None + + def write(self, line: str | None) -> None: + if not line: + return + line = line.strip() + if not line or line in self.seen: + return + self.seen.add(line) + if self.fh is None: + self.path.parent.mkdir(parents=True, exist_ok=True) + self.fh = self.path.open("w", encoding="utf-8") + self.fh.write(line + "\n") + + def close(self) -> None: + if self.fh is not None: + self.fh.close() + + +def _collect_sentences( + data: dict, + file_rules: dict[str, str], + canon=lambda x: x, + canonicalize_refs=lambda x: x, + promoted: "set[str] | None" = None, +): + """Yields (intent_name, sample, rules) tuples one at a time, where + `rules` is the rule set in scope for that sample — _common.yaml plus + file-level plus per-block overrides (hassil scopes rules that way). + + Rules whose names are in ``promoted`` are skipped on re-introduction: + a per-block expansion_rules entry that shadows an already-promoted + `.voc` rule would otherwise undo the promotion and reinstate the + inlined body, undoing the cartesian-explosion mitigation.""" + promoted = promoted or set() + for name, body in (data.get("intents") or {}).items(): + for block in body.get("data", []) or []: + block_rules = dict(file_rules) + for rname, rtmpl in (block.get("expansion_rules") or {}).items(): + slug = _slug(canon(rname)) + if slug in promoted: + continue # don't undo a global .voc promotion + if isinstance(rtmpl, list): + rtmpl = "(" + "|".join(rtmpl) + ")" + block_rules[slug] = canonicalize_refs(str(rtmpl)) + for sample in block.get("sentences", []) or []: + yield name, sample, block_rules + + +def _collect_responses(data: dict): + """Yields (intent_name, response_key, phrase). The response key (e.g. + 'default', 'lights_area') discriminates the scenario the skill code + is responding to, so each key becomes its own .dialog file.""" + for name, body in (data.get("responses", {}).get("intents") or {}).items(): + for key, value in (body or {}).items(): + if isinstance(value, str): + yield name, key, value + elif isinstance(value, list): + for v in value: + yield name, key, str(v) + + +# --------------------------------------------------------------------------- +# Driver +# --------------------------------------------------------------------------- + + +def convert( + src: Path, + lang: str, + dst: Path, + resume: bool = True, + report: "object | None" = None, + check: bool = False, +) -> None: + """Convert one language. If ``report`` is None a private TSV is opened + next to this script; otherwise the caller's file handle is reused + (so a multi-language run lands in a single audit log).""" + out = dst / "locale" / lang + out.mkdir(parents=True, exist_ok=True) + + sentences_dir = src / "sentences" / lang + responses_dir = src / "responses" / lang + if not sentences_dir.is_dir(): + raise SystemExit(f"no sentences for {lang!r} at {sentences_dir}") + + common: dict = {} + common_path = sentences_dir / "_common.yaml" + if common_path.is_file(): + common = yaml.safe_load(common_path.read_text(encoding="utf-8")) or {} + + # Lists and expansion-rule keys may be non-ASCII (e.g. Spanish + # `habitación`, Czech `pokoj`); slug them so the OVOS lint accepts + # the resulting resource names. All downstream code operates in + # slug-space — `_expand_rules` and `_rewrite_slot` also slug their + # captured names so references match up. + # + # Rule names additionally pass through the per-language canonical + # name map so the resulting `.voc` files land at English topic + # names (`turn_off.voc`, `open.voc`, …) instead of the local- + # language original (`apaga.voc`, `obre.voc`, …). + # Map keys are looked up in slug-space so both the accented hassil + # original (`habitación`) and the ASCII form (`habitacion`) resolve + # to the same canonical entry. + lang_canonical = { + _slug(k): v for k, v in CANONICAL_RULE_NAMES.get(lang, {}).items() + } + # Drop mappings whose canonical target collides with an existing + # local rule name. E.g. in `ro/_common.yaml` both `brightness` (a + # slot-wrapper) and `luminozitatea` (the noun) exist; mapping the + # latter to `brightness` would silently overwrite the former when + # the multi-source parent gets synthesised. Better to keep the + # local name in that case and let the user resolve manually. + existing_sources = { + _slug(k) for k in (common.get("expansion_rules") or {}) + } + for src in list(lang_canonical): + target = _slug(lang_canonical[src]) + if target != src and target in existing_sources: + del lang_canonical[src] + + def _canon(name: str) -> str: + return lang_canonical.get(_slug(name), name) + + # `_canonicalize_refs` rewrites `` → `` in template + # text. When called on a rule body that belongs to a multi-source + # canonical group, references to *sibling* sources are preserved as + # local-named so the parent .voc can list them without creating a + # cycle through the canonical name. The `own_canonical` argument + # carries the rule's own group name; pass None for sample text. + def _canonicalize_refs(s: str, own_canonical: str | None = None) -> str: + if not lang_canonical: + return s + + def _sub(m: re.Match[str]) -> str: + name = m.group(1) + target_canon = _canon(name) + target_slug = _slug(target_canon) + if own_canonical is not None and target_slug == own_canonical: + # Sibling reference — keep it local so the canonical + # parent's `` listing terminates. + return f"<{_slug(name)}>" + return f"<{target_slug}>" + + return _RULE_RE.sub(_sub, s) + + lists = {_slug(k): v for k, v in (common.get("lists") or {}).items()} + raw_rules_yaml = dict(common.get("expansion_rules") or {}) + + # Hoist per-file and per-data-block `expansion_rules:` into the + # global rule pool. Many hassil corpora define helper rules at + # narrow scope (e.g. `quant_queda` and `dades` in `ca`'s timer + # status block) but they're still subject to the same path-count + # explosion when inlined. Lifting them lets the promotion pipeline + # turn them into `.voc` files. `_common.yaml` definitions still + # take precedence on name collision. + for path in sorted(sentences_dir.glob("*.yaml")): + if path.name == "_common.yaml": + continue + try: + sub = yaml.safe_load(path.read_text(encoding="utf-8")) or {} + except Exception: + continue + for rname, rtmpl in (sub.get("expansion_rules") or {}).items(): + raw_rules_yaml.setdefault(rname, rtmpl) + for body in (sub.get("intents") or {}).values(): + for block in body.get("data", []) or []: + for rname, rtmpl in (block.get("expansion_rules") or {}).items(): + raw_rules_yaml.setdefault(rname, rtmpl) + + # Two-pass rule loading so that multiple local-language rules can + # collapse into one canonical English concept: + # + # * If only one source rule maps to a canonical name, we just + # rename it (existing behaviour). + # * If two or more source rules map to the same canonical name, + # each keeps its own local-named .voc file (with the synonyms + # intact) and we synthesise a parent .voc named after the + # canonical English concept whose body is `(||...)` — + # the OVOS-style "vocabulary of vocabularies" pattern. + # First pass: collect which sources map to which canonical, so we + # can pass the right `own_canonical` context into body rewrites. + canonical_to_sources: dict[str, list[str]] = {} + raw_bodies: dict[str, str] = {} + for name, template in raw_rules_yaml.items(): + if isinstance(template, list): + template = "(" + "|".join(template) + ")" + src_slug = _slug(name) + can_slug = _slug(_canon(name)) + canonical_to_sources.setdefault(can_slug, []).append(src_slug) + raw_bodies[src_slug] = (str(template), can_slug) + + raw_rules: dict[str, str] = {} + for canonical, sources in canonical_to_sources.items(): + if len(sources) == 1: + body, _ = raw_bodies[sources[0]] + raw_rules[canonical] = _canonicalize_refs(body) + else: + # Keep each source rule under its own local name. Internal + # `` references stay local-named so the parent + # composite below terminates without cycling. + for src in sources: + body, _ = raw_bodies[src] + raw_rules[src] = _canonicalize_refs(body, own_canonical=canonical) + # Add a parent rule referencing each source by local name. + raw_rules[canonical] = "(" + "|".join(f"<{s}>" for s in sources) + ")" + + # Recursively resolve rules into final bodies. While resolving, any + # rule whose enumerated alternation grows past PROMOTE_RULE_THRESHOLD + # is replaced by a `{name}` slot placeholder and its literal value + # set is captured in `promoted_values` for later `.entity` emission. + rules: dict[str, str] = {} + # promoted_values: rule name → enumerated literal value set. For + # auto-promoted slot-free rules this becomes a .voc (vocabulary + # referenced as ``). FORCE_PROMOTE rules contain `{slot}`s + # and can't be enumerated, so they go straight to free-form `{name}` + # capture slots with no value-set file. + promoted_values: dict[str, list[str]] = {} + promoted_voc: set[str] = set() + visiting: set[str] = set() + + def _resolve(name: str) -> str: + if name in rules: + return rules[name] + if name in visiting: + return f"<{name}>" # cycle — leave unresolved + # Hard-coded promotion: free-form capture slot, no enumeration. + if name in FORCE_PROMOTE: + rules[name] = "{" + name + "}" + return rules[name] + body = raw_rules.get(name) + if body is None: + return f"<{name}>" + visiting.add(name) + + def _sub(m: re.Match[str]) -> str: + inner = _slug(m.group(1)) + if inner not in raw_rules: + return f"<{inner}>" # unresolved, but slug-canonicalised + r = _resolve(inner) + # `{...}` or `<...>` placeholders interpolate as-is; rule + # bodies need parens so the caller's surrounding operators + # see one token. + if r.startswith("{") and r.endswith("}"): + return r + if r.startswith("<") and r.endswith(">"): + return r + return "(" + r + ")" + + resolved = _RULE_RE.sub(_sub, body) + visiting.discard(name) + + # Promotion: slot-free body + alternation count above threshold + + # enumeration fits the entity cap. + if "{" not in resolved: + # The cartesian-path counter is an upper bound — it + # over-multiplies sibling alternations. Use it only for the + # lower-bound check, then actually attempt enumeration with + # a cap. If enumeration fits, the rule promotes regardless + # of how the estimator scored it. + if _cartesian_paths(resolved) >= PROMOTE_RULE_THRESHOLD: + try: + values = _enumerate(resolved, MAX_PROMOTED_VALUES) + except OverflowError: + values = [] + if 0 < len(values) <= MAX_PROMOTED_VALUES: + if _voc_looks_garbage(values): + # Let it stay inlined; cartesian cap or lint will + # catch pathological samples later. + pass + else: + promoted_values[name] = values + promoted_voc.add(name) + rules[name] = f"<{name}>" + return rules[name] + rules[name] = resolved + return resolved + + for n in list(raw_rules): + _resolve(n) + # Auto-promoted rules emit as `.voc` and the `` reference stays + # in the sample — remove them from the inline-substitution table so + # `_expand_rules` doesn't loop on the self-reference placeholder. + for n in promoted_voc: + rules.pop(n, None) + + slot_to_list: dict[str, str] = {} + + # Stats for --check mode + stats_seen: dict[str, int] = {} + stats_kept: dict[str, int] = {} + + # Audit log: every rejected sample/response/entity, with reason. The + # log lives next to this script so it's easy to find and not buried + # under the output tree. + owns_report = report is None + if owns_report: + report_path = Path(__file__).resolve().parent / "convert_hassil_intents.skipped.tsv" + report = report_path.open("w", encoding="utf-8") + report.write("lang\tkind\tintent\treason\toriginal\n") + else: + report_path = Path(getattr(report, "name", "")) + skipped = {"sample": 0, "response": 0, "entity": 0} + + def _log(kind: str, intent: str, reason: str, original: str) -> None: + skipped[kind] = skipped.get(kind, 0) + 1 + # TSV — strip tabs/newlines from the original so each record is one row. + clean = original.replace("\t", " ").replace("\n", " ").replace("\r", " ") + report.write(f"{lang}\t{kind}\t{intent}\t{reason}\t{clean}\n") + + # sentences//*.yaml → .intent (streamed) + # OVOS-INTENT-1 v3 relaxed §5.5: `.intent` files now allow templates + # with different slot sets (union semantics). We stream directly to + # one writer per target intent. Hassil spreads samples for one + # intent across multiple yaml files, so the writer must stay open + # for the whole conversion. + intent_writers: dict[Path, _StreamWriter] = {} + skip_targets: set[Path] = set() + try: + for path in sorted(sentences_dir.glob("*.yaml")): + if path.name == "_common.yaml": + continue + data = yaml.safe_load(path.read_text(encoding="utf-8")) or {} + file_rules = dict(rules) + for rname, rtmpl in (data.get("expansion_rules") or {}).items(): + slug = _slug(_canon(rname)) + if slug in promoted_voc: + continue + if isinstance(rtmpl, list): + rtmpl = "(" + "|".join(rtmpl) + ")" + file_rules[slug] = _canonicalize_refs(str(rtmpl)) + for intent_name, sample, sample_rules in _collect_sentences( + data, file_rules, _canon, _canonicalize_refs, promoted_voc + ): + stats_seen[intent_name] = stats_seen.get(intent_name, 0) + 1 + target = out / f"{_snake_case(intent_name)}.intent" + if target in skip_targets: + continue + if target not in intent_writers: + if resume and target.exists(): + skip_targets.add(target) + continue + intent_writers[target] = _StreamWriter(target) + sample = _canonicalize_refs(sample) + lines, reason = rewrite_template(sample, sample_rules, slot_to_list) + if lines is None: + _log("sample", intent_name, reason or "unknown", sample) + continue + for line in lines: + intent_writers[target].write(line) + stats_kept[intent_name] = stats_kept.get(intent_name, 0) + 1 + finally: + for w in intent_writers.values(): + w.close() + + # responses//*.yaml → .dialog. A hassil response yaml has two + # axes of variation that each become its own .dialog file: + # + # * response keys (`default`, `area`, `lights_area`, …) — distinct + # scenarios the skill code branches on + # * Jinja `{% if … %} A {% elif … %} B {% else %} C {% endif %}` + # branches inside one response — distinct conditions the skill + # code also branches on + # + # The skill calls `self.speak_dialog(name)` with the name matching + # the scenario + condition the runtime is in, mirroring the original + # Jinja branch selection. + # + # Naming: + # + # .dialog default key, no Jinja + # _branch_.dialog default key, Jinja + # _.dialog other key, no Jinja + # __branch_.dialog other key, Jinja + # + # Branch indices are 1-based and follow the source order of the + # `if`/`elif`/`else` block. + file_lines: dict[Path, list[str]] = {} + if responses_dir.is_dir(): + # Group raw phrases by (intent, key) first so per-key branch + # counts are consistent across multiple list-valued entries. + by_key: dict[tuple[str, str], list[list[str]]] = {} + for path in sorted(responses_dir.glob("*.yaml")): + data = yaml.safe_load(path.read_text(encoding="utf-8")) or {} + for intent_name, key, phrase in _collect_responses(data): + branches, reason = _normalise_response(phrase) + if branches is None: + _log("response", intent_name, reason or "unknown", phrase) + continue + by_key.setdefault((intent_name, key), []).append(branches) + + for (intent_name, key), phrase_branches in by_key.items(): + base = _snake_case(intent_name) + key_suffix = "" if key == "default" else f"_{_snake_case(key)}" + max_branches = max(len(b) for b in phrase_branches) + for bi in range(max_branches): + if max_branches == 1: + target = out / f"{base}{key_suffix}.dialog" + else: + target = out / f"{base}{key_suffix}_branch_{bi + 1}.dialog" + bucket = file_lines.setdefault(target, []) + for branches in phrase_branches: + if bi < len(branches): + bucket.append(branches[bi]) + + for target, lines in file_lines.items(): + if resume and target.exists(): + continue + w = _StreamWriter(target) + try: + for line in lines: + w.write(line) + finally: + w.close() + + # .voc — one per auto-promoted slot-free expansion rule. These are + # vocabularies (alternations of synonyms), not capture slots. + voc_written = 0 + for name in sorted(promoted_voc): + target = out / f"{name}.voc" + if resume and target.exists(): + voc_written += 1 + continue + vw = _StreamWriter(target) + try: + for v in promoted_values[name]: + vw.write(v) + finally: + vw.close() + if target.exists(): + voc_written += 1 + + # .entity (one per slot encountered) — streamed too. + entities_written = 0 + for slot, list_name in sorted(slot_to_list.items()): + target = out / f"{slot}.entity" + if resume and target.exists(): + entities_written += 1 + continue + # FORCE_PROMOTE rules with nested slots are free-form captures + # — they have no materialisable value set. + if slot in promoted_voc: + continue # already emitted as .voc + body = lists.get(list_name) + values = _list_values(body) if body is not None else None + if not values: + # No materialisable value set — but the slot still appears + # in the .intent file, where OVOS treats a slot without an + # .entity as a free-form capture. Wildcard / undefined lists + # are expected to degrade this way, so they don't deserve a + # row in the hard-failure audit. Only true blowups (range + # too large to materialise, etc.) are logged. + if isinstance(body, dict) and body.get("range"): + _log("entity", slot, f"range_too_large (> {MAX_ENTITY_VALUES} values)", str(body)) + continue + ew = _StreamWriter(target) + try: + for v in values: + ew.write(v) + finally: + ew.close() + if target.exists(): + entities_written += 1 + + # Seed area.entity with common names if the language is covered and + # the file does not already exist (e.g. from a materialised hassil list). + area_entity = out / "area.entity" + if not (resume and area_entity.exists()) and lang in COMMON_AREA_NAMES: + aw = _StreamWriter(area_entity) + try: + for name in COMMON_AREA_NAMES[lang]: + aw.write(name) + finally: + aw.close() + + if owns_report: + report.close() + total = sum(skipped.values()) + print( + f"[{lang}] wrote {out} — " + f"slots: {len(slot_to_list)}, entities: {entities_written}, " + f"vocabularies: {voc_written}, " + f"skipped: {skipped.get('sample', 0)}/{skipped.get('response', 0)}/" + f"{skipped.get('entity', 0)} (sample/response/entity), " + f"audit rows: {total}" + ) + + if check: + thin = [ + (i, stats_seen[i], stats_kept.get(i, 0)) + for i in stats_seen + if stats_seen[i] > 0 and stats_kept.get(i, 0) / stats_seen[i] < 0.5 + ] + thin.sort(key=lambda x: x[2] / x[1]) + if thin: + print(f" --check: intents with <50% sample survival --") + for intent, seen, kept in thin: + print(f" {intent}: {kept}/{seen} ({kept/seen*100:.1f}%)") + + +def convert_all( + src: Path, dst: Path, resume: bool = True, check: bool = False +) -> None: + """Convert every language under ``src/sentences/``. All languages + share one audit TSV with a leading `lang` column.""" + sentences_root = src / "sentences" + langs = sorted(d.name for d in sentences_root.iterdir() if d.is_dir()) + if not langs: + raise SystemExit(f"no language directories under {sentences_root}") + report_path = Path(__file__).resolve().parent / "convert_hassil_intents.skipped.tsv" + with report_path.open("w", encoding="utf-8") as report: + report.write("lang\tkind\tintent\treason\toriginal\n") + for lang in langs: + try: + convert(src, lang, dst, resume=resume, report=report, check=check) + except SystemExit as e: + print(f"[{lang}] skipped: {e}") + print(f"audit log: {report_path}") + + +def main() -> None: + import argparse + + parser = argparse.ArgumentParser( + description="Convert OHF-Voice/intents (hassil) into an OVOS-INTENT-2 locale tree." + ) + parser.add_argument("src", type=Path, help="Path to OHF-Voice/intents checkout") + parser.add_argument("lang", help="Language code or 'all'") + parser.add_argument("dst", type=Path, help="Output directory") + parser.add_argument( + "--check", action="store_true", help="Print coverage report per intent" + ) + parser.add_argument( + "--no-resume", + dest="resume", + action="store_false", + default=True, + help="Overwrite existing files instead of resuming", + ) + args = parser.parse_args() + if args.lang == "all": + convert_all(args.src, args.dst, resume=args.resume, check=args.check) + else: + convert( + args.src, args.lang, args.dst, resume=args.resume, check=args.check + ) + + +if __name__ == "__main__": + main() diff --git a/examples/export_hf_dataset.py b/examples/export_hf_dataset.py new file mode 100644 index 00000000..bdd08c55 --- /dev/null +++ b/examples/export_hf_dataset.py @@ -0,0 +1,340 @@ +"""Export a hassil-locale tree (from convert_hassil_intents.py) into an +OVOS-compatible HuggingFace dataset with three configs per language: + + * ``{lang}-templates`` — one row per ``.intent`` line, with slot schema + * ``{lang}-keywords`` — one row per ``.voc`` vocabulary set + * ``{lang}-entities`` — one row per ``.entity`` value set + * ``{lang}-test`` — expanded realisations (template × entity combinations) + +The schema mirrors ``OpenVoiceOS/massive-templates`` and +``OpenVoiceOS/intents-for-eval`` so downstream training pipelines +(Padacioso, Adapt, m2v, …) can consume the data unchanged. + +Usage:: + + python export_hf_dataset.py /tmp/hassil-locale /tmp/hassil-dataset + +The output directory receives one JSONL file per (lang, config) pair: + + /tmp/hassil-dataset/ + en-US-templates.jsonl + en-US-keywords.jsonl + en-US-entities.jsonl + en-US-test.jsonl + pt-PT-templates.jsonl + ... + +A minimal ``dataset_info.json`` is also written so the folder can be +uploaded directly with ``huggingface-cli upload-folder``. +""" +from __future__ import annotations + +import json +import os +import re +import sys +from pathlib import Path + +# Optional: use ovos-spec-tools expansion for bracket alternation / optionals +try: + from ovos_spec_tools.expansion import expand, MalformedTemplate +except Exception: + expand = None # type: ignore[assignment] + MalformedTemplate = Exception # type: ignore[misc,assignment] + + +SLOT_RE = re.compile(r"\{([a-zA-Z_][a-zA-Z0-9_]*)\}") +VOC_RE = re.compile(r"<([a-zA-Z_][a-zA-Z0-9_]*)>") + + +def _extract_slots(template: str) -> list[str]: + """Return ordered slot names from a template.""" + return SLOT_RE.findall(template) + + +def _extract_voc_refs(template: str) -> list[str]: + """Return ordered vocabulary references from a template.""" + return VOC_RE.findall(template) + + +def _expand_alternations(template: str) -> list[str]: + """Expand ``(a|b)`` and ``[opt]`` using ovos-spec-tools when available, + otherwise return the template unchanged.""" + if expand is None: + return [template] + try: + return expand(template) + except MalformedTemplate: + return [template] + + +def _load_locale_file(path: Path) -> list[str]: + """Read a locale file, drop comments and blank lines.""" + lines: list[str] = [] + with path.open(encoding="utf-8") as fh: + for raw in fh: + line = raw.split("#", 1)[0].strip() + if line: + lines.append(line) + return lines + + +def _build_slot_schema( + slot_names: list[str], + entity_dir: Path, +) -> list[dict]: + """For each slot name, try to find a matching ``.entity`` file and load + example values.""" + schema: list[dict] = [] + for name in slot_names: + entity_path = entity_dir / f"{name}.entity" + examples: list[str] = [] + if entity_path.is_file(): + examples = _load_locale_file(entity_path)[:20] # cap examples + schema.append({"name": name, "examples": examples}) + return schema + + +def _realise_template( + template: str, + slot_names: list[str], + entity_dir: Path, + max_combos: int = 50, +) -> list[tuple[str, dict[str, str | None]]]: + """Generate concrete utterances by filling slots with entity values. + Returns ``(utterance, slot_map)`` pairs.""" + if not slot_names: + return [(template, {})] + + # Load value pools for each slot + pools: list[list[str]] = [] + for name in slot_names: + entity_path = entity_dir / f"{name}.entity" + if entity_path.is_file(): + values = _load_locale_file(entity_path) + pools.append(values[:10]) # cap per slot + else: + pools.append([f"__{name}__"]) # placeholder when no entity file + + # Cartesian product capped + from itertools import product + results: list[tuple[str, dict[str, str | None]]] = [] + for combo in product(*pools): + if len(results) >= max_combos: + break + utterance = template + slot_map: dict[str, str | None] = {} + for name, value in zip(slot_names, combo): + utterance = utterance.replace("{" + name + "}", value, 1) + slot_map[name] = value if not value.startswith("__") else None + results.append((utterance, slot_map)) + return results + + +def export_templates( + locale_dir: Path, + lang: str, + out_dir: Path, +) -> int: + """Write ``{lang}-templates.jsonl`` — one row per ``.intent`` line.""" + lang_dir = locale_dir / lang + if not lang_dir.is_dir(): + return 0 + + entity_dir = lang_dir + rows: list[dict] = [] + + for intent_file in sorted(lang_dir.glob("*.intent")): + intent_name = intent_file.stem + # Domain is always "homeassistant" for this corpus + domain = "homeassistant" + for template in _load_locale_file(intent_file): + slot_names = _extract_slots(template) + slots = _build_slot_schema(slot_names, entity_dir) + rows.append( + { + "intent_id": f"{domain}:{intent_name}", + "domain": domain, + "template": template, + "slots": slots, + } + ) + + out_path = out_dir / f"{lang}-templates.jsonl" + with out_path.open("w", encoding="utf-8") as fh: + for row in rows: + fh.write(json.dumps(row, ensure_ascii=False) + "\n") + return len(rows) + + +def export_keywords( + locale_dir: Path, + lang: str, + out_dir: Path, +) -> int: + """Write ``{lang}-keywords.jsonl`` — one row per ``.voc`` file, + packaged as an Adapt-style rule.""" + lang_dir = locale_dir / lang + if not lang_dir.is_dir(): + return 0 + + rows: list[dict] = [] + for voc_file in sorted(lang_dir.glob("*.voc")): + vocab_name = voc_file.stem + values = _load_locale_file(voc_file) + # Package as a keyword rule (required vocab + optional vocab pattern) + rows.append( + { + "intent_id": f"homeassistant:{vocab_name}", + "domain": "homeassistant", + "required_vocab": values, + "optional_vocab": [], + "excluded_vocab": [], + } + ) + + out_path = out_dir / f"{lang}-keywords.jsonl" + with out_path.open("w", encoding="utf-8") as fh: + for row in rows: + fh.write(json.dumps(row, ensure_ascii=False) + "\n") + return len(rows) + + +def export_entities( + locale_dir: Path, + lang: str, + out_dir: Path, +) -> int: + """Write ``{lang}-entities.jsonl`` — one row per ``.entity`` file.""" + lang_dir = locale_dir / lang + if not lang_dir.is_dir(): + return 0 + + rows: list[dict] = [] + for entity_file in sorted(lang_dir.glob("*.entity")): + slot_name = entity_file.stem + values = _load_locale_file(entity_file) + rows.append( + { + "slot_name": slot_name, + "values": values, + "type": "closed" if values else "open", + } + ) + + out_path = out_dir / f"{lang}-entities.jsonl" + with out_path.open("w", encoding="utf-8") as fh: + for row in rows: + fh.write(json.dumps(row, ensure_ascii=False) + "\n") + return len(rows) + + +def export_test( + locale_dir: Path, + lang: str, + out_dir: Path, +) -> int: + """Write ``{lang}-test.jsonl`` — expanded realisations with gold intent + slots.""" + lang_dir = locale_dir / lang + if not lang_dir.is_dir(): + return 0 + + entity_dir = lang_dir + rows: list[dict] = [] + seen_utterances: set[str] = set() + + for intent_file in sorted(lang_dir.glob("*.intent")): + intent_name = intent_file.stem + domain = "homeassistant" + for template in _load_locale_file(intent_file): + # First expand alternations/optionals + expanded = _expand_alternations(template) + for exp in expanded: + slot_names = _extract_slots(exp) + realised = _realise_template(exp, slot_names, entity_dir, max_combos=20) + for utterance, slot_map in realised: + if utterance in seen_utterances: + continue + seen_utterances.add(utterance) + rows.append( + { + "utterance": utterance, + "expected_intent": f"{domain}:{intent_name}", + "expected_slots": slot_map, + "lang": lang, + } + ) + + out_path = out_dir / f"{lang}-test.jsonl" + with out_path.open("w", encoding="utf-8") as fh: + for row in rows: + fh.write(json.dumps(row, ensure_ascii=False) + "\n") + return len(rows) + + +def export_all( + locale_dir: Path, + out_dir: Path, + langs: list[str] | None = None, +) -> dict[str, dict[str, int]]: + """Export every language under ``locale_dir``.""" + out_dir.mkdir(parents=True, exist_ok=True) + if langs is None: + langs = sorted(d.name for d in locale_dir.iterdir() if d.is_dir()) + + stats: dict[str, dict[str, int]] = {} + for lang in langs: + print(f"[{lang}] exporting...") + stats[lang] = { + "templates": export_templates(locale_dir, lang, out_dir), + "keywords": export_keywords(locale_dir, lang, out_dir), + "entities": export_entities(locale_dir, lang, out_dir), + "test": export_test(locale_dir, lang, out_dir), + } + return stats + + +def _write_dataset_info(out_dir: Path, stats: dict[str, dict[str, int]]) -> None: + """Write a minimal dataset_info.json for HF upload.""" + configs: list[dict] = [] + for lang in sorted(stats): + for suffix in ["templates", "keywords", "entities", "test"]: + configs.append( + { + "config_name": f"{lang}-{suffix}", + "data_files": f"{lang}-{suffix}.jsonl", + "num_examples": stats[lang][suffix], + } + ) + + info = { + "dataset_name": "hassil-ovos-locale", + "description": "Home Assistant hassil intents exported to OVOS-INTENT-2 locale format", + "configs": configs, + "languages": sorted(stats), + } + with (out_dir / "dataset_info.json").open("w", encoding="utf-8") as fh: + json.dump(info, fh, indent=2, ensure_ascii=False) + + +def main() -> None: + if len(sys.argv) != 3: + print(__doc__) + print("Usage: python export_hf_dataset.py ") + raise SystemExit(2) + locale_dir = Path(sys.argv[1]) + out_dir = Path(sys.argv[2]) + stats = export_all(locale_dir, out_dir) + _write_dataset_info(out_dir, stats) + + total_rows = sum(sum(v.values()) for v in stats.values()) + print(f"\nDone — {len(stats)} languages, {total_rows:,} total rows in {out_dir}") + for lang, s in sorted(stats.items()): + print( + f" {lang:6}: templates={s['templates']:>5}, keywords={s['keywords']:>4}, " + f"entities={s['entities']:>3}, test={s['test']:>5}" + ) + + +if __name__ == "__main__": + main() diff --git a/examples/generate_entities.py b/examples/generate_entities.py new file mode 100644 index 00000000..dbd14e32 --- /dev/null +++ b/examples/generate_entities.py @@ -0,0 +1,476 @@ +"""Generate .entity files for all slots that lack them across the +hassil-locale tree. Populates language-specific value sets where +possible and falls back to English for universal HA constants.""" +from __future__ import annotations + +import json +import os +from pathlib import Path +from collections import defaultdict + +# --------------------------------------------------------------------------- +# Slot value definitions +# --------------------------------------------------------------------------- + +# Numeric ranges — same digits in every language +NUMERIC_SLOTS: dict[str, range] = { + "brightness": range(0, 101), + "percentage": range(0, 101), + "position": range(0, 101), + "volume_level": range(0, 101), + "volume_step": range(1, 21), + "temperature": range(0, 101), + "hours": range(0, 24), + "minutes": range(0, 60), + "seconds": range(0, 60), + "start_hours": range(0, 24), + "start_minutes": range(0, 60), +} + +# Home Assistant internal state values (language-agnostic constants) +HA_STATES: list[str] = [ + "on", "off", "open", "closed", "locked", "unlocked", + "opening", "closing", "detected", "clear", "charging", + "not_charging", "connected", "disconnected", "home", + "away", "running", "not_running", "safe", "unsafe", + "update_available", "up_to_date", "low", "normal", + "wet", "dry", "cold", "hot", "present", "not_present", +] + +HA_DOMAINS: list[str] = [ + "light", "fan", "switch", "cover", "climate", "media_player", + "sensor", "binary_sensor", "lock", "vacuum", "timer", + "input_boolean", "scene", "script", "automation", + "weather", "camera", " humidifier", "water_heater", +] + +HA_DEVICE_CLASSES: list[str] = [ + "awning", "blind", "curtain", "door", "garage", "gate", + "shade", "shutter", "window", "battery", "carbon_monoxide", + "cold", "connectivity", "door", "garage_door", "gas", "heat", + "light", "lock", "moisture", "motion", "occupancy", "opening", + "plug", "power", "presence", "problem", "running", "safety", + "smoke", "sound", "tamper", "update", "vibration", +] + +COLORS: list[str] = [ + "white", "black", "red", "orange", "yellow", "green", + "blue", "purple", "brown", "pink", "turquoise", +] + +MEDIA_CLASSES: list[str] = [ + "artist", "album", "track", "song", "playlist", + "podcast", "movie", "tv_show", +] + +# Temporal expressions — English examples (best-effort; translators can localize) +TIMER_DURATIONS: list[str] = [ + "1 minute", "5 minutes", "10 minutes", "15 minutes", + "30 minutes", "1 hour", "2 hours", "3 hours", +] + +TIMER_STARTS: list[str] = [ + "in 1 minute", "in 5 minutes", "in 10 minutes", + "in 1 hour", "in 2 hours", "at 3 pm", "at noon", +] + +# Common device name patterns (examples; real devices are user-defined) +DEVICE_NAME_EXAMPLES: list[str] = [ + "kitchen light", "living room light", "bedroom light", + "front door", "garage door", "back gate", + "thermostat", "tv", "speaker", +] + +# Shopping / todo items +ITEM_EXAMPLES: list[str] = [ + "milk", "bread", "eggs", "coffee", "apples", +] + +# Free-form examples +MESSAGE_EXAMPLES: list[str] = [ + "hello", "dinner is ready", "the laundry is done", +] + +SEARCH_QUERY_EXAMPLES: list[str] = [ + "the beatles", "jazz", "news", +] + +CONVERSATION_COMMAND_EXAMPLES: list[str] = [ + "remind me to call mom", "set a timer", +] + +RESPONSE_EXAMPLES: list[str] = [ + "yes", "no", "ok", "sure", +] + +FLOOR_EXAMPLES: list[str] = [ + "ground floor", "first floor", "second floor", "basement", "attic", +] + +# --------------------------------------------------------------------------- +# Per-language overrides (where we have translations) +# --------------------------------------------------------------------------- + +_LANG_OVERRIDES: dict[str, dict[str, list[str]]] = { + # Portuguese (PT) — examples + "pt": { + "state": ["ligado", "desligado", "aberto", "fechado", "trancado", "destrancado"], + "color": ["branco", "preto", "vermelho", "laranja", "amarelo", "verde", "azul", "roxo", "castanho", "rosa", "turquesa"], + }, + "pt-BR": { + "state": ["ligado", "desligado", "aberto", "fechado", "trancado", "destrancado"], + "color": ["branco", "preto", "vermelho", "laranja", "amarelo", "verde", "azul", "roxo", "marrom", "rosa", "turquesa"], + }, + "es": { + "state": ["encendido", "apagado", "abierto", "cerrado", "bloqueado", "desbloqueado"], + "color": ["blanco", "negro", "rojo", "naranja", "amarillo", "verde", "azul", "púrpura", "marrón", "rosa", "turquesa"], + }, + "fr": { + "state": ["allumé", "éteint", "ouvert", "fermé", "verrouillé", "déverrouillé"], + "color": ["blanc", "noir", "rouge", "orange", "jaune", "vert", "bleu", "violet", "marron", "rose", "turquoise"], + }, + "de": { + "state": ["an", "aus", "offen", "geschlossen", "verriegelt", "entriegelt"], + "color": ["weiß", "schwarz", "rot", "orange", "gelb", "grün", "blau", "lila", "braun", "pink", "türkis"], + }, + "it": { + "state": ["acceso", "spento", "aperto", "chiuso", "bloccato", "sbloccato"], + "color": ["bianco", "nero", "rosso", "arancione", "giallo", "verde", "blu", "viola", "marrone", "rosa", "turchese"], + }, + "nl": { + "state": ["aan", "uit", "open", "gesloten", "vergrendeld", "ontgrendeld"], + "color": ["wit", "zwart", "rood", "oranje", "geel", "groen", "blauw", "paars", "bruin", "roze", "turquoise"], + }, + "ca": { + "state": ["encès", "apagat", "obert", "tancat", "bloquejat", "desbloquejat"], + "color": ["blanc", "negre", "vermell", "taronja", "groc", "verd", "blau", "porpra", "marró", "rosa", "turquesa"], + }, + "da": { + "state": ["til", "fra", "åben", "lukket", "låst", "oplåst"], + "color": ["hvid", "sort", "rød", "orange", "gul", "grøn", "blå", "lilla", "brun", "pink", "turkis"], + }, + "sv": { + "state": ["på", "av", "öppen", "stängd", "låst", "olåst"], + "color": ["vit", "svart", "röd", "orange", "gul", "grön", "blå", "lila", "brun", "rosa", "turkos"], + }, + "nb": { + "state": ["på", "av", "åpen", "lukket", "låst", "opplåst"], + "color": ["hvit", "svart", "rød", "oransje", "gul", "grønn", "blå", "lilla", "brun", "rosa", "turkis"], + }, + "fi": { + "state": ["päällä", "pois", "auki", "kiinni", "lukittu", "avattu"], + "color": ["valkoinen", "musta", "punainen", "oranssi", "keltainen", "vihreä", "sininen", "violetti", "ruskea", "vaaleanpunainen", "turkoosi"], + }, + "pl": { + "state": ["włączony", "wyłączony", "otwarty", "zamknięty", "zamknięty", "otwarty"], + "color": ["biały", "czarny", "czerwony", "pomarańczowy", "żółty", "zielony", "niebieski", "fioletowy", "brązowy", "różowy", "turkusowy"], + }, + "ru": { + "state": ["включено", "выключено", "открыто", "закрыто", "заблокировано", "разблокировано"], + "color": ["белый", "чёрный", "красный", "оранжевый", "жёлтый", "зелёный", "синий", "фиолетовый", "коричневый", "розовый", "бирюзовый"], + }, + "ja": { + "state": ["オン", "オフ", "開", "閉", "施錠", "解錠"], + "color": ["白", "黒", "赤", "橙", "黄", "緑", "青", "紫", "茶", "桃", "水色"], + }, + "ko": { + "state": ["켜짐", "꺼짐", "열림", "닫힘", "잠김", "잠금해제"], + "color": ["흰색", "검은색", "빨간색", "주황색", "노란색", "초록색", "파란색", "보라색", "갈색", "분홍색", "청록색"], + }, + "zh-CN": { + "state": ["开", "关", "打开", "关闭", "锁定", "解锁"], + "color": ["白色", "黑色", "红色", "橙色", "黄色", "绿色", "蓝色", "紫色", "棕色", "粉色", "青色"], + }, + "ar": { + "state": ["مفعل", "معطل", "مفتوح", "مغلق", "مقفل", "مفتوح"], + "color": ["أبيض", "أسود", "أحمر", "برتقالي", "أصفر", "أخضر", "أزرق", "بنفسجي", "بني", "وردي", "تركواز"], + }, + "he": { + "state": ["דלוק", "כבוי", "פתוח", "סגור", "נעול", "פתוח"], + "color": ["לבן", "שחור", "אדום", "כתום", "צהוב", "ירוק", "כחול", "סגול", "חום", "ורוד", "טורקיז"], + }, + "tr": { + "state": ["açık", "kapalı", "açık", "kapalı", "kilitli", "kilitli açık"], + "color": ["beyaz", "siyah", "kırmızı", "turuncu", "sarı", "yeşil", "mavi", "mor", "kahverengi", "pembe", "turkuaz"], + }, + "th": { + "state": ["เปิด", "ปิด", "เปิด", "ปิด", "ล็อค", "ปลดล็อค"], + "color": ["ขาว", "ดำ", "แดง", "ส้ม", "เหลือง", "เขียว", "น้ำเงิน", "ม่วง", "น้ำตาล", "ชมพู", "ฟ้า"], + }, + "vi": { + "state": ["bật", "tắt", "mở", "đóng", "khóa", "mở khóa"], + "color": ["trắng", "đen", "đỏ", "cam", "vàng", "xanh lá", "xanh dương", "tím", "nâu", "hồng", "ngọc"], + }, + "id": { + "state": ["hidup", "mati", "terbuka", "tertutup", "terkunci", "terbuka"], + "color": ["putih", "hitam", "merah", "oranye", "kuning", "hijau", "biru", "ungu", "coklat", "merah muda", "biru toska"], + }, + "ms": { + "state": ["hidup", "mati", "buka", "tutup", "kunci", "buka"], + "color": ["putih", "hitam", "merah", "oren", "kuning", "hijau", "biru", "ungu", "perang", "merah jambu", "biru turquoise"], + }, + "ro": { + "state": ["pornit", "oprit", "deschis", "închis", "blocat", "deblocat"], + "color": ["alb", "negru", "roșu", "portocaliu", "galben", "verde", "albastru", "mov", "maro", "roz", "turcoaz"], + }, + "el": { + "state": ["ανοικτό", "κλειστό", "ανοικτό", "κλειστό", "κλειδωμένο", "ξεκλείδωτο"], + "color": ["λευκό", "μαύρο", "κόκκινο", "πορτοκαλί", "κίτρινο", "πράσινο", "μπλε", "μωβ", "καφέ", "ροζ", "τυρκουάζ"], + }, + "hu": { + "state": ["be", "ki", "nyitva", "zárva", "zárva", "nyitva"], + "color": ["fehér", "fekete", "piros", "narancssárga", "sárga", "zöld", "kék", "lila", "barna", "rózsaszín", "türkiz"], + }, + "cs": { + "state": ["zapnuto", "vypnuto", "otevřeno", "zavřeno", "zamčeno", "odemčeno"], + "color": ["bílá", "černá", "červená", "oranžová", "žlutá", "zelená", "modrá", "fialová", "hnědá", "růžová", "tyrkysová"], + }, + "sk": { + "state": ["zapnuté", "vypnuté", "otvorené", "zatvorené", "zamknuté", "odomknuté"], + "color": ["biela", "čierna", "červená", "oranžová", "žltá", "zelená", "modrá", "fialová", "hnedá", "ružová", "tyrkysová"], + }, + "sl": { + "state": ["vklopljeno", "izklopljeno", "odprto", "zaprto", "zaklenjeno", "odklenjeno"], + "color": ["bela", "črna", "rdeča", "oranžna", "rumena", "zelena", "modra", "vijolična", "rjava", "roza", "turkizna"], + }, + "hr": { + "state": ["uključeno", "isključeno", "otvoreno", "zatvoreno", "zaključano", "otključano"], + "color": ["bijela", "crna", "crvena", "narančasta", "žuta", "zelena", "plava", "ljubičasta", "smeđa", "ružičasta", "tirkizna"], + }, + "sr": { + "state": ["укључено", "искључено", "отворено", "затворено", "закључано", "откључано"], + "color": ["бела", "црна", "црвена", "наранџаста", "жута", "зелена", "плава", "љубичаста", "смеђа", "ружа", "тиркизна"], + }, + "sr-Latn": { + "state": ["uključeno", "isključeno", "otvoreno", "zatvoreno", "zaključano", "otključano"], + "color": ["bela", "crna", "crvena", "narandžasta", "žuta", "zelena", "plava", "ljubičasta", "smeđa", "ružičasta", "tirkizna"], + }, + "bg": { + "state": ["включено", "изключено", "отворено", "затворено", "заключено", "отключено"], + "color": ["бял", "черен", "червен", "оранжев", "жълт", "зелен", "син", "лилав", "кафяв", "розов", "тюркоаз"], + }, + "uk": { + "state": ["увімкнено", "вимкнено", "відкрито", "закрито", "заблоковано", "розблоковано"], + "color": ["білий", "чорний", "червоний", "помаранчевий", "жовтий", "зелений", "синій", "фіолетовий", "коричневий", "рожевий", "бірюзовий"], + }, + "et": { + "state": ["sees", "väljas", "avatud", "suletud", "lukustatud", "avatud"], + "color": ["valge", "must", "punane", "oranž", "kollane", "roheline", "sinine", "lilla", "pruun", "roosa", "türkiis"], + }, + "lt": { + "state": [["įjungta", "išjungta", "atidaryta", "uždaryta", "užrakinta", "atrakinta"]], + "color": ["balta", "juoda", "raudona", "oranžinė", "geltona", "žalia", "mėlyna", "violetinė", "rudа", "rožinė", "turkio"], + }, + "lv": { + "state": ["ieslēgts", "izslēgts", "atvērts", "aizvērts", "aizslēgts", "atslēgts"], + "color": ["balts", "melns", "sarkans", "oranžs", "dzeltens", "zaļš", "zils", "violets", "brūns", "rozā", "tirkīzs"], + }, + "is": { + "state": ["á", "af", "opið", "lokað", "læst", "opnað"], + "color": ["hvítur", "svartur", "rauður", "appelsínugulur", "gulur", "grænn", "blár", "fjólublár", "brúnn", "bleikur", "grænblár"], + }, + "ga": { + "state": ["ar", "as", "oscailte", "dúnta", "faoi ghlas", "oscailte"], + "color": ["bán", "dubh", "dearg", "oraiste", "buí", "glas", "gorm", "corcra", "donn", "bándearg", "turcais"], + }, + "cy": { + "state": ["ymlaen", "i ffwrdd", "agored", "ar gau", "wedi'i gloi", "wedi'i datgloi"], + "color": ["gwyn", "du", "coch", "oren", "melyn", "gwyrdd", "glas", "porffor", "brown", "pinc", "torcwys"], + }, + "af": { + "state": ["aan", "af", "oop", "toe", "gesluit", "oopgesluit"], + "color": ["wit", "swart", "rooi", "oranje", "geel", "groen", "blou", "pers", "bruin", "pienk", "turkoois"], + }, + "sw": { + "state": ["wazi", "zima", "funguliwa", "fungwa", "kufungwa", "kufunguliwa"], + "color": ["nyeupe", "nyeusi", "nyekundu", "machungwa", "manjano", "kijani", "bluu", "zambarau", "kahawia", "waridi", "turquoise"], + }, + "eu": { + "state": ["piztuta", "itzalita", "irekita", "itxita", "blokeatuta", "desblokeatuta"], + "color": ["zuri", "beltz", "gorri", "laranja", "hori", "berde", "urdin", "more", "marroi", "arrosa", "turkesa"], + }, + "gl": { + "state": ["encendido", "apagado", "aberto", "pechado", "bloqueado", "desbloqueado"], + "color": ["branco", "negro", "vermello", "laranxa", "amarelo", "verde", "azul", "púrpura", "marrón", "rosa", "turquesa"], + }, + "fa": { + "state": ["روشن", "خاموش", "باز", "بسته", "قفل شده", "باز شده"], + "color": ["سفید", "سیاه", "قرمز", "نارنجی", "زرد", "سبز", "آبی", "بنفش", "قهوه‌ای", "صورتی", "فیروزه‌ای"], + }, + "ne": { + "state": ["खुला", "बन्द", "खुला", "बन्द", "बन्द", "खुला"], + "color": ["सेतो", "कालो", "रातो", "सुन्तला", "पहेँलो", "हरियो", "नीलो", "प्याजी", "खैरो", "गुलाबी", "हरियो नीलो"], + }, + "ka": { + "state": ["ჩართული", "გამორთული", "გახსნილი", "დახურული", "დაკეტილი", "გახსნილი"], + "color": ["თეთრი", "შავი", "წითელი", "ნარინჯისფერი", "ყვითელი", "მწვანე", "ლურჯი", "იისფერი", "ყავისფერი", "ვარდისფერი", "ფირუზისფერი"], + }, + "bn": { + "state": ["চালু", "বন্ধ", "খোলা", "বন্ধ", "বন্ধ", "খোলা"], + "color": ["সাদা", "কালো", "লাল", "কমলা", "হলুদ", "সবুজ", "নীল", "বেগুনি", "বাদামি", "গোলাপি", "ফিরোজা"], + }, + "gu": { + "state": ["ચાલુ", "બંધ", "ખુલ્લું", "બંધ", "બંધ", "ખુલ્લું"], + "color": ["સફેદ", "કાળો", "લાલ", "નારંગી", "પીળો", "લીલો", "વાદળી", "વાયલેટ", "તપખમ", "ગુલાબી", "ફિરોઝા"], + }, + "hi": { + "state": ["चालू", "बंद", "खुला", "बंद", "बंद", "खुला"], + "color": ["सफेद", "काला", "लाल", "नारंगी", "पीला", "हरा", "नीला", "बैंगनी", "भूरा", "गुलाबी", "फिरोजा"], + }, + "kn": { + "state": ["ಆನ್", "ಆಫ್", "ತೆರೆ", "ಮುಚ್ಚು", "ಮುಚ್ಚು", "ತೆರೆ"], + "color": ["ಬಿಳಿ", "ಕಪ್ಪ", "ಕೆಂಪು", "ಕಿಟಕಿ", "ಹಳದಿ", "ಹಸಿರು", "ನೀಲಿ", "ನೇರಳೆ", "ಕಂದು", "ಗುಲಾಬಿ", "ಟರ್ಕಿಶ್"], + }, + "ml": { + "state": ["ഓൺ", "ഓഫ്", "തുറന്ന", "അടച്ച", "പൂട്ടിയ", "തുറന്ന"], + "color": ["വെള്ള", "കറുപ്പ്", "ചുവപ്പ്", "ഓറഞ്ച്", "മഞ്ഞ", "പച്ച", "നീല", "ഊദ", "തവിട്ട്", "ചുവപ്പ്", "പച്ചനീല"], + }, + "mr": { + "state": ["चालू", "बंद", "उघड", "बंद", "बंद", "उघड"], + "color": ["पांढरा", "काळा", "लाल", "केशरी", "पिवळा", "हिरवा", "निळा", "जांभळा", "तपकिरी", "गुलाबी", "फिरोजा"], + }, + "pa": { + "state": ["ਚਾਲੂ", "ਬੰਦ", "ਖੁੱਲ੍ਹਾ", "ਬੰਦ", "ਬੰਦ", "ਖੁੱਲ੍ਹਾ"], + "color": ["ਚਿੱਟਾ", "ਕਾਲਾ", "ਲਾਲ", "ਨਾਰੰਗੀ", "ਪੀਲਾ", "ਹਰਾ", "ਨੀਲਾ", "ਜਾਮਨੀ", "ਭੂਰਾ", "ਗੁਲਾਬੀ", "ਫਿਰੋਜ਼ਾ"], + }, + "ta": { + "state": ["இயக்கத்தில்", "அணை", "திறந்த", "மூடிய", "பூட்டிய", "திறந்த"], + "color": ["வெள்ளை", "கருப்பு", "சிவப்பு", "செம்மஞ்சள்", "மஞ்சள்", "பச்சை", "நீலம்", "ஊதா", "பழுப்பு", "சிவப்பு", "கடல் பச்சை"], + }, + "te": { + "state": ["ఆన్", "ఆఫ్", "తెరిచిన", "మూసిన", "మూసిన", "తెరిచిన"], + "color": ["తెలుపు", "నలుపు", "ఎరుపు", "నారింజ", "పసుపు", "ఆకుపచ్చ", "నీలం", "ఊదా", "ముదురు", "ఎరుపు", "ఆకుపచ్చ నీలం"], + }, + "ur": { + "state": ["آن", "آف", "کھلا", "بند", "بند", "کھلا"], + "color": ["سفید", "سیاہ", "سرخ", "نارنجی", "پیلا", "سبز", "نیلا", "بنفشی", "بھورا", "گلابی", "فیروزی"], + }, + "mn": { + "state": ["ассан", "унтраасан", "нээгдсэн", "хаалттай", "түгжсэн", "нээгдсэн"], + "color": ["цагаан", "хар", "улаан", "улбар шар", "шар", "ногоон", "хөх", "хөхөвтөр", "бор", "ягаан", "түрquoise"], + }, + "kw": { + "state": ["yn-mara", "marow", "ygerys", "degesys", "gwlyk", "digorys"], + "color": ["gwynn", "du", "rudh", "oren", "melyn", "glas", "glas", "glas", "gwyrdh", "gwyrdh", "glas"], + }, + "lb": { + "state": ["un", "aus", "op", "zou", "gespaart", "op"], + "color": ["wäiss", "schwaarz", "rout", "orange", "giel", "gréng", "blo", "mof", "brong", "rosa", "turkoois"], + }, + "pl": { + "state": ["włączony", "wyłączony", "otwarty", "zamknięty", "zamknięty", "otwarty"], + "color": ["biały", "czarny", "czerwony", "pomarańczowy", "żółty", "zielony", "niebieski", "fioletowy", "brązowy", "różowy", "turkusowy"], + }, +} + +# Fix lt state +if "lt" in _LANG_OVERRIDES: + _LANG_OVERRIDES["lt"]["state"] = ["įjungta", "išjungta", "atidaryta", "uždaryta", "užrakinta", "atrakinta"] + + +def _get_values(slot: str, lang: str) -> list[str] | None: + """Return a value list for ``slot`` in ``lang``, or ``None`` if the slot + should remain a free-form wildcard.""" + # Check language-specific overrides first + overrides = _LANG_OVERRIDES.get(lang, {}) + if slot in overrides: + return overrides[slot] + + # Numeric ranges + if slot in NUMERIC_SLOTS: + return [str(n) for n in NUMERIC_SLOTS[slot]] + + # Universal HA constants + if slot == "state": + return HA_STATES + if slot == "domain": + return HA_DOMAINS + if slot == "device_class": + return HA_DEVICE_CLASSES + if slot == "color": + return COLORS + if slot == "media_class": + return MEDIA_CLASSES + + # Temporal (English examples as fallback) + if slot == "timer_duration": + return TIMER_DURATIONS + if slot == "timer_start": + return TIMER_STARTS + + # Example-based slots (return examples rather than leaving empty) + if slot == "name": + return DEVICE_NAME_EXAMPLES + if slot == "item": + return ITEM_EXAMPLES + if slot == "message": + return MESSAGE_EXAMPLES + if slot == "search_query": + return SEARCH_QUERY_EXAMPLES + if slot == "conversation_command": + return CONVERSATION_COMMAND_EXAMPLES + if slot == "response": + return RESPONSE_EXAMPLES + if slot == "floor": + return FLOOR_EXAMPLES + + # Unknown slot — leave as wildcard (no .entity file) + return None + + +def generate_missing_entities(locale_dir: Path) -> dict[str, int]: + """Walk the locale tree and write ``.entity`` files for every slot that + appears in ``.intent`` files but has no matching ``.entity`` file.""" + stats: dict[str, int] = {} + langs = [d for d in locale_dir.iterdir() if d.is_dir()] + + for lang_dir in sorted(langs): + lang = lang_dir.name + intent_files = list(lang_dir.glob("*.intent")) + if not intent_files: + continue + + # Discover which slots are used + used_slots: set[str] = set() + for intent_file in intent_files: + with intent_file.open(encoding="utf-8") as fh: + for line in fh: + for match in re.finditer(r"\{([a-zA-Z_][a-zA-Z0-9_]*)\}", line): + used_slots.add(match.group(1)) + + written = 0 + for slot in sorted(used_slots): + entity_path = lang_dir / f"{slot}.entity" + if entity_path.exists(): + continue + values = _get_values(slot, lang) + if values is None: + continue + with entity_path.open("w", encoding="utf-8") as fh: + for v in values: + fh.write(v + "\n") + written += 1 + + if written: + stats[lang] = written + + return stats + + +import re + +def main() -> None: + import sys + if len(sys.argv) != 2: + print("Usage: python generate_entities.py ") + raise SystemExit(2) + locale_dir = Path(sys.argv[1]) + stats = generate_missing_entities(locale_dir) + total = sum(stats.values()) + print(f"Wrote {total} .entity files across {len(stats)} languages.") + for lang, count in sorted(stats.items()): + print(f" {lang:8}: {count} files") + + +if __name__ == "__main__": + main() diff --git a/examples/hf_dataset.py b/examples/hf_dataset.py new file mode 100644 index 00000000..e87bcfa0 --- /dev/null +++ b/examples/hf_dataset.py @@ -0,0 +1,95 @@ +"""Load templates from an OVOS HuggingFace dataset, expand them, and export +to an OVOS-INTENT-2 locale directory. + +Usage:: + + # Export English hassil intents to locale + python examples/hf_dataset.py hassil-intents en /tmp/my-locale + + # Expand a few templates to see concrete utterances + python examples/hf_dataset.py hassil-intents en /tmp/my-locale --expand + + # Export massive-templates for Portuguese + python examples/hf_dataset.py massive-templates pt-PT /tmp/pt-locale +""" +from __future__ import annotations + +import sys +from pathlib import Path + +from ovos_spec_tools.datasets import ( + SUPPORTED_DATASETS, + expand_hf_template, + export_to_locale, + load_dataset_templates, +) + + +def main(argv: list[str] | None = None) -> int: + import argparse + + parser = argparse.ArgumentParser(description=__doc__) + parser.add_argument( + "dataset", + choices=list(SUPPORTED_DATASETS), + help="HF dataset to load", + ) + parser.add_argument( + "lang", + help="Language tag (e.g. en, pt_BR, en-US, pt-PT)", + ) + parser.add_argument( + "output", + type=Path, + help="Output directory for locale tree", + ) + parser.add_argument( + "--expand", + action="store_true", + help="Show expanded utterances for first 3 templates", + ) + parser.add_argument( + "--split", + default="train", + help="Dataset split (default: train)", + ) + parser.add_argument( + "--max-samples", + type=int, + default=10, + help="Max samples to show per expanded template (default: 10)", + ) + args = parser.parse_args(argv) + + print(f"Loading {args.dataset} ({SUPPORTED_DATASETS[args.dataset]}) / {args.lang} ...") + templates = load_dataset_templates( + args.dataset, lang=args.lang, split=args.split + ) + print(f"Loaded {len(templates)} templates") + + if args.expand and templates: + print("\n--- Template expansion samples ---") + for i, row in enumerate(templates[:3]): + tpl = row["template"] + exps = row.get("expansions", []) + results = expand_hf_template(tpl, exps, max_samples=args.max_samples) + print(f"\n [{i}] intent_id={row['intent_id']}") + print(f" template: {tpl}") + if exps: + print(f" expansions: {len(exps)} keywords") + if results: + print(f" sample utterances ({len(results)} total):") + for r in results[:args.max_samples]: + print(f" - {r}") + + count = export_to_locale( + args.dataset, lang=args.lang, output_dir=args.output, split=args.split + ) + loc = args.output / "locale" / args.lang + print(f"\nExported {count} template lines to {loc}/") + + return 0 + + +if __name__ == "__main__": + raise SystemExit(main()) diff --git a/examples/locale_to_hf_dataset.py b/examples/locale_to_hf_dataset.py new file mode 100644 index 00000000..34494582 --- /dev/null +++ b/examples/locale_to_hf_dataset.py @@ -0,0 +1,224 @@ +"""Convert any OVOS-INTENT-2 locale tree into a HuggingFace dataset in +the style of https://huggingface.co/datasets/OpenVoiceOS/intents-for-eval. + +Works on any directory containing `/*.intent` resource files — +the converter's `examples/hassil-locale/`, a single skill's `locale/`, +a multi-skill workspace, etc. The script auto-detects whether the +input path is the locale parent (`/locale//…`) or already +the locale root (`//…`). + +Emits a single flat JSONL plus a README: + + /train_templates.jsonl + /README.md + +Per-row schema (`domain` is only emitted if a domain is supplied): + + {intent_id, lang, template, slots: [{name, examples}]} + {intent_id, domain, lang, template, slots: [{name, examples}]} + +Run: + python examples/locale_to_hf_dataset.py [domain] + + # default — no domain field on rows + python examples/locale_to_hf_dataset.py examples/hassil-locale examples/hf-dataset + + # add a constant domain to every row + python examples/locale_to_hf_dataset.py examples/hassil-locale out smarthome +""" +from __future__ import annotations + +import json +import re +import sys +from pathlib import Path + +SLOT_RE = re.compile(r"\{([^{}]+)\}") +VOC_RE = re.compile(r"<([^<>\s]+)>") + +# --------------------------------------------------------------------------- +# Locale loading +# --------------------------------------------------------------------------- + + +def _load_lines(path: Path) -> list[str]: + return [ + ln.strip() for ln in path.read_text(encoding="utf-8").splitlines() + if ln.strip() and not ln.strip().startswith("#") + ] + + +def _load_locale(lang_dir: Path): + """Return (vocabs, entities, intents) for one language directory. + + `vocabs` and `entities` map name → list[str]; `intents` maps base + name → list[str] (the template lines).""" + vocabs: dict[str, list[str]] = {} + entities: dict[str, list[str]] = {} + intents: dict[str, list[str]] = {} + for path in sorted(lang_dir.iterdir()): + if path.suffix == ".voc": + vocabs[path.stem] = _load_lines(path) + elif path.suffix == ".entity": + entities[path.stem] = _load_lines(path) + elif path.suffix == ".intent": + intents[path.stem] = _load_lines(path) + return vocabs, entities, intents + + +# --------------------------------------------------------------------------- +# Template processing +# --------------------------------------------------------------------------- + + +def _expand_vocab_inline(template: str, vocabs: dict[str, list[str]]) -> str: + """Replace `` references with `(v1|v2|v3)` alternations so the + output template carries only `{slot}` placeholders and OVOS-INTENT-1 + alternation/optional syntax — matching the intents-for-eval style.""" + + def _sub(m: re.Match[str]) -> str: + name = m.group(1) + values = vocabs.get(name) + if not values: + return m.group(0) + return "(" + "|".join(values) + ")" + + # Resolve up to a few levels — a .voc could in principle reference + # another vocabulary; in practice that doesn't happen in the + # hassil-derived corpus, but cap iterations defensively. + for _ in range(4): + new = VOC_RE.sub(_sub, template) + if new == template: + break + template = new + return template + + +def _slots_for_template( + template: str, entities: dict[str, list[str]] +) -> list[dict]: + seen: dict[str, dict] = {} + for slot in SLOT_RE.findall(template): + if slot in seen: + continue + seen[slot] = {"name": slot, "examples": entities.get(slot, [])[:64]} + return list(seen.values()) + + +# --------------------------------------------------------------------------- +# Driver +# --------------------------------------------------------------------------- + + +def _json_line(obj) -> str: + return json.dumps(obj, ensure_ascii=False, separators=(",", ": ")) + + +def export_lang( + lang_dir: Path, lang: str, f_tpl, domain: str | None +) -> int: + vocabs, entities, intents = _load_locale(lang_dir) + count = 0 + for intent_name in sorted(intents): + intent_id = f"{domain}:{intent_name}" if domain else intent_name + for tpl in intents[intent_name]: + expanded = _expand_vocab_inline(tpl, vocabs) + row: dict = {"intent_id": intent_id} + if domain: + row["domain"] = domain + row["lang"] = lang + row["template"] = expanded + row["slots"] = _slots_for_template(expanded, entities) + f_tpl.write(_json_line(row) + "\n") + count += 1 + return count + + +_README = """\ +# OVOS-INTENT-2 derived intents dataset + +Mechanically converted from an OVOS-INTENT-2 locale tree +(`/*.intent`, `*.voc`, `*.entity` files) into the +[OpenVoiceOS/intents-for-eval][src] templates schema. + +[src]: https://huggingface.co/datasets/OpenVoiceOS/intents-for-eval + +## Layout + + train_templates.jsonl # all languages interleaved; `lang` selects + +## Row schema + + { + "intent_id": "turn_on", + "lang": "en", + "template": "(turn on|switch on) [the] {name}", + "slots": [ + {"name": "name", "examples": ["kitchen light", "..."]} + ] + } + +If a domain was supplied at conversion time, every row carries a +`domain` field (`intent_id` is prefixed as `:`). + +## What the converter does + + * One row per template line in `/.intent`. + * `` references are expanded inline into `(a|b|c)` + alternations from the matching `.voc` file. + * `{slot}` references survive verbatim; their `examples` come from + `.entity` (empty list if the slot is free-form). + * `[opt]` optionals and `(a|b)` alternations are preserved. + +## Caveats vs intents-for-eval + + * Templates are mechanically derived from the source locale — + alternations and optionals reflect what the source authored, + not paraphrase-style native-speaker authoring. + * No test set is included. Evaluation buckets (`paraphrase`, + `near_ood`, `far_ood`, `asr_noise`, `typos`) require human + authoring and are out of scope for this conversion. +""" + + +def _resolve_locale_root(src: Path) -> Path: + """Return the directory whose children are `/` dirs. Accepts + either the dataset root (`/locale//…`) or the locale root + itself (`//…`).""" + if (src / "locale").is_dir(): + return src / "locale" + # Otherwise expect `//…`; the heuristic is that at least + # one child contains a .intent file. + for child in src.iterdir(): + if child.is_dir() and any(child.glob("*.intent")): + return src + raise SystemExit(f"no `/*.intent` resources found under {src}") + + +def main() -> None: + if len(sys.argv) not in (3, 4): + print(__doc__) + raise SystemExit(2) + src, dst = Path(sys.argv[1]), Path(sys.argv[2]) + domain = sys.argv[3] if len(sys.argv) == 4 else None + locale_root = _resolve_locale_root(src) + dst.mkdir(parents=True, exist_ok=True) + (dst / "README.md").write_text(_README, encoding="utf-8") + + out_path = dst / "train_templates.jsonl" + total = 0 + langs = 0 + with out_path.open("w", encoding="utf-8") as f_tpl: + for lang_dir in sorted(p for p in locale_root.iterdir() if p.is_dir()): + if not any(lang_dir.glob("*.intent")): + continue + lang = lang_dir.name + count = export_lang(lang_dir, lang, f_tpl, domain) + print(f"[{lang}] templates: {count:>5}") + total += count + langs += 1 + print(f"total: {langs} languages, {total} rows — at {out_path}") + + +if __name__ == "__main__": + main() diff --git a/examples/reexport_recursive.py b/examples/reexport_recursive.py new file mode 100644 index 00000000..c95148f9 --- /dev/null +++ b/examples/reexport_recursive.py @@ -0,0 +1,109 @@ +"""Recursively resolve nested references inside expansion values, +on a per-row basis. A keyword is only expanded if it appears in the same +row's template (or in another expansion value that is itself being expanded).""" +from __future__ import annotations + +import json +import re +from pathlib import Path +import sys + + +def _load_all_vocabs(locale_dir: Path, lang: str) -> dict[str, list[str]]: + voc_dir = locale_dir / lang + vocabs: dict[str, list[str]] = {} + if not voc_dir.is_dir(): + return vocabs + for voc_path in voc_dir.glob("*.voc"): + stem = voc_path.stem + vals = [ln.strip() for ln in voc_path.read_text(encoding="utf-8").splitlines() if ln.strip()] + if vals: + vocabs[stem] = vals + return vocabs + + +def _extract_refs(text: str) -> set[str]: + return set(re.findall(r"<([a-zA-Z_][a-zA-Z0-9_]*)>", text)) + + +def _resolve_value(value: str, vocabs: dict[str, list[str]], seen: set[str]) -> list[str]: + """Recursively expand references in a single value string. + ``seen`` prevents cycles.""" + refs = _extract_refs(value) + if not refs: + return [value] + + # Pick the first ref that is available in vocabs and not already seen + for ref in sorted(refs): + if ref in seen or ref not in vocabs: + continue + sub_values = vocabs[ref] + results: list[str] = [] + for sub in sub_values: + new_val = value.replace(f"<{ref}>", sub, 1) + results.extend(_resolve_value(new_val, vocabs, seen | {ref})) + return results + + # Unresolvable refs remain as-is + return [value] + + +def _resolve_expansions(template: str, vocabs: dict[str, list[str]]) -> list[dict[str, object]]: + """For every in ``template``, resolve its .voc values recursively.""" + refs = _extract_refs(template) + if not refs: + return [] + + resolved: list[dict[str, object]] = [] + for ref in sorted(refs): + if ref not in vocabs: + continue + raw_values = vocabs[ref] + flat_values: list[str] = [] + for v in raw_values: + flat_values.extend(_resolve_value(v, vocabs, {ref})) + # Deduplicate while preserving order + seen = set() + deduped = [v for v in flat_values if not (v in seen or seen.add(v))] + resolved.append({"keyword": ref, "values": deduped}) + return resolved + + +def reexport_recursive(locale_dir: Path, input_dir: Path, output_dir: Path) -> None: + output_dir.mkdir(parents=True, exist_ok=True) + langs = sorted(p.name for p in input_dir.iterdir() if p.is_dir()) + + for lang in langs: + in_path = input_dir / lang / "templates.jsonl" + if not in_path.exists(): + continue + lang_dir = output_dir / lang + lang_dir.mkdir(parents=True, exist_ok=True) + out_path = lang_dir / "templates.jsonl" + + vocabs = _load_all_vocabs(locale_dir, lang) + written = 0 + with in_path.open(encoding="utf-8") as fin, out_path.open("w", encoding="utf-8") as fout: + for line in fin: + row = json.loads(line) + template = row.get("template", "") + expansions = _resolve_expansions(template, vocabs) + if expansions: + row["expansions"] = expansions + else: + row.pop("expansions", None) + fout.write(json.dumps(row, ensure_ascii=False) + "\n") + written += 1 + + print(f"[{lang}] wrote {written} rows -> {out_path}") + + +def main() -> None: + if len(sys.argv) != 4: + print("Usage: python reexport_recursive.py ") + raise SystemExit(2) + reexport_recursive(Path(sys.argv[1]), Path(sys.argv[2]), Path(sys.argv[3])) + + +if __name__ == "__main__": + main() diff --git a/examples/reexport_uniform.py b/examples/reexport_uniform.py new file mode 100644 index 00000000..761c0cf4 --- /dev/null +++ b/examples/reexport_uniform.py @@ -0,0 +1,70 @@ +"""Re-export templates into per-language subdirectories with uniform +expansions as list instead of dynamic dict keys.""" +from __future__ import annotations + +import json +import re +from pathlib import Path +import sys + + +def _load_vocabs(locale_dir: Path, lang: str) -> dict[str, list[str]]: + voc_dir = locale_dir / lang + vocabs: dict[str, list[str]] = {} + if not voc_dir.is_dir(): + return vocabs + for voc_path in voc_dir.glob("*.voc"): + stem = voc_path.stem + vals = [ln.strip() for ln in voc_path.read_text(encoding="utf-8").splitlines() if ln.strip()] + if vals: + vocabs[stem] = vals + return vocabs + + +def _extract_keyword_refs(template: str) -> list[str]: + return re.findall(r"<([a-zA-Z_][a-zA-Z0-9_]*)>", template) + + +def reexport_uniform(locale_dir: Path, input_dir: Path, output_dir: Path) -> None: + output_dir.mkdir(parents=True, exist_ok=True) + langs = sorted(p.stem.replace("-templates", "") for p in input_dir.glob("*-templates.jsonl")) + + for lang in langs: + in_path = input_dir / f"{lang}-templates.jsonl" + lang_dir = output_dir / lang + lang_dir.mkdir(parents=True, exist_ok=True) + out_path = lang_dir / "templates.jsonl" + + vocabs = _load_vocabs(locale_dir, lang) + written = 0 + with in_path.open(encoding="utf-8") as fin, out_path.open("w", encoding="utf-8") as fout: + for line in fin: + row = json.loads(line) + # Remove old dict-style expansions if present + row.pop("expansions", None) + + template = row.get("template", "") + refs = _extract_keyword_refs(template) + expansions: list[dict[str, object]] = [] + for ref in refs: + vals = vocabs.get(ref) + if vals: + expansions.append({"keyword": ref, "values": vals}) + if expansions: + row["expansions"] = expansions + + fout.write(json.dumps(row, ensure_ascii=False) + "\n") + written += 1 + + print(f"[{lang}] wrote {written} rows -> {out_path}") + + +def main() -> None: + if len(sys.argv) != 4: + print("Usage: python reexport_uniform.py ") + raise SystemExit(2) + reexport_uniform(Path(sys.argv[1]), Path(sys.argv[2]), Path(sys.argv[3])) + + +if __name__ == "__main__": + main() diff --git a/ovos_spec_tools/__init__.py b/ovos_spec_tools/__init__.py index 7af43cff..c48e821e 100644 --- a/ovos_spec_tools/__init__.py +++ b/ovos_spec_tools/__init__.py @@ -109,6 +109,19 @@ ) from ovos_spec_tools.version import __version__ +try: + from ovos_spec_tools.datasets import ( + SUPPORTED_DATASETS, + expand_hf_template, + export_to_locale, + load_dataset_templates, + ) +except ImportError: + SUPPORTED_DATASETS = {} + expand_hf_template = None # type: ignore[assignment] + export_to_locale = None # type: ignore[assignment] + load_dataset_templates = None # type: ignore[assignment] + __all__ = [ "Message", "MalformedMessage", @@ -156,5 +169,9 @@ "SPEC_TO_LEGACY", "migration_counterpart", "NamespaceTranslator", + "load_dataset_templates", + "expand_hf_template", + "export_to_locale", + "SUPPORTED_DATASETS", "__version__", ] diff --git a/ovos_spec_tools/datasets.py b/ovos_spec_tools/datasets.py new file mode 100644 index 00000000..ea408b5d --- /dev/null +++ b/ovos_spec_tools/datasets.py @@ -0,0 +1,352 @@ +"""Load, expand, and export HuggingFace datasets conforming to the +OVOS-INTENT-2 template syntax. + +Supports three datasets: + +- ``hassil-intents`` — ``OpenVoiceOS/hass-intent-templates`` (per-lang configs, + ``template`` with full OVOS syntax, ``expansions`` column) +- ``intents-for-eval`` — ``OpenVoiceOS/intents-for-eval`` + (``{lang}-templates`` config, ``template`` has ```` inlined to ``(a|b|c)``) +- ``massive-templates`` — ``OpenVoiceOS/massive-templates`` + (``{lang}-templates`` config, same style as intents-for-eval) + +Usage:: + + from ovos_spec_tools.datasets import ( + load_dataset_templates, + expand_hf_template, + export_to_locale, + SUPPORTED_DATASETS, + ) + + # Load English templates from hassil-intents + templates = load_dataset_templates("hassil-intents", lang="en") + + # Expand a single template into concrete utterances + results = expand_hf_template( + template=" [the] {name}", + expansions=[{"keyword": "turn_on", "values": ["turn on", "switch on"]}], + ) + + # Export to OVOS-INTENT-2 locale directory + export_to_locale("hassil-intents", lang="en", output_dir="/tmp/my-locale") +""" +from __future__ import annotations + +import json +import re +import typing +from collections import defaultdict +from pathlib import Path + +from ovos_spec_tools.expansion import expand as _expand_templates + +if typing.TYPE_CHECKING: + from collections.abc import Sequence + +SLOT_RE = re.compile(r"\{([^{}]+)\}") +VOC_RE = re.compile(r"<([^<>\s]+)>") + +# --------------------------------------------------------------------------- +# Dataset registry +# --------------------------------------------------------------------------- + +SUPPORTED_DATASETS: dict[str, str] = { + "hassil-intents": "OpenVoiceOS/hassil-intents-locale", + "intents-for-eval": "OpenVoiceOS/intents-for-eval", + "massive-templates": "OpenVoiceOS/massive-templates", +} + +_REQUIRED_EXTRAS = ( + "The `datasets` library is required; install it with: pip install datasets" +) + + +def _resolve_config(dataset_id: str, lang: str) -> str: + """Return the HF config name for ``dataset_id`` at ``lang``. + + * ``hassil-intents`` uses plain language tags (``en``, ``pt_BR``, …). + * ``intents-for-eval`` and ``massive-templates`` use the form + ``{lang}-templates`` (``en-US-templates``, ``pt-PT-templates``, …). + + Accepts either form as input for the latter two. + """ + if dataset_id == "hassil-intents": + return lang + # intents-for-eval / massive-templates: accept short or full form + if lang.endswith("-templates"): + return lang + # Normalize BCP-47 style: "en-US" -> "en-US-templates" + return f"{lang}-templates" + + +def _normalize_rows(rows: list[dict], dataset_id: str) -> list[dict]: + """Normalize rows from any supported dataset to a common schema:: + + {"intent_id": str, "template": str, "slots": [{name, examples}], + "expansions": [{keyword, values}]} + + For datasets that inline ```` refs into ``(a|b|c)`` groups, + ``expansions`` is left empty and the template carries the alternations + directly. + """ + normalized: list[dict] = [] + for row in rows: + out: dict = { + "intent_id": row.get("intent_id") or row.get("intent") or "", + "template": row.get("template") or "", + "slots": row.get("slots") or [], + } + if "expansions" in row: + out["expansions"] = row["expansions"] or [] + elif "text" in row: + out["text"] = row["text"] + normalized.append(out) + return normalized + + +# --------------------------------------------------------------------------- +# Public API +# --------------------------------------------------------------------------- + + +def load_dataset_templates( + dataset_id: str, + lang: str = "en", + *, + split: str = "train", + streaming: bool = True, +) -> list[dict]: + """Load template rows from a supported HuggingFace dataset. + + Parameters + ---------- + dataset_id + One of ``"hassil-intents"``, ``"intents-for-eval"``, + ``"massive-templates"``. + lang + Language tag. For hassil-intents use plain codes (``en``, ``pt_BR``); + for the others use BCP-47 (``en-US``, ``pt-PT``) — or the full config + name (``en-US-templates``). Default ``"en"``. + split + Dataset split to load. Default ``"train"``. + streaming + If True (default) iterate in streaming mode. + + Returns + ------- + list[dict] + Normalized rows with keys ``intent_id``, ``template``, ``slots``, + and (if available) ``expansions``. + """ + try: + from datasets import load_dataset + except ImportError: + raise ImportError(_REQUIRED_EXTRAS) + + repo = SUPPORTED_DATASETS.get(dataset_id) + if repo is None: + raise ValueError( + f"Unknown dataset {dataset_id!r}; choose from {list(SUPPORTED_DATASETS)}" + ) + + config = _resolve_config(dataset_id, lang) + ds = load_dataset(repo, config, split=split, streaming=streaming) + rows: list[dict] = [dict(r) for r in ds] + return _normalize_rows(rows, dataset_id) + + +def expand_hf_template( + template: str, + expansions: Sequence[dict] | None = None, + max_samples: int = 2048, +) -> list[str]: + """Expand an OVOS-INTENT-2 template into concrete utterances. + + ```` references are resolved from ``expansions`` (a list of + ``{"keyword": str, "values": [str, ...]}`` dicts, as found in the + hassil-intents dataset). If ``expansions`` is empty or None, resolves + ```` from inline ``(a|b|c)`` groups (as in intents-for-eval / + massive-templates style). + + Then expands alternatives ``(a|b)``, optionals ``[x]``, and fills + ``{slot}`` placeholders (which render as-fill since they are opaque + to the grammar). + + Parameters + ---------- + template + An OVOS-INTENT-2 template string. + expansions + Optional list of ``{keyword, values}`` dicts providing the vocabulary + for each ```` reference. + max_samples + Hard cap on the number of sample utterances to generate. + + Returns + ------- + list[str] + Concrete utterance strings. + """ + if expansions: + vocab: dict[str, list[str]] = {} + for entry in expansions: + kw = entry.get("keyword", "") + vals = entry.get("values", []) + if kw and vals: + vocab[kw] = list(vals) + else: + vocab = None + + try: + results = _expand_templates(template, vocabularies=vocab) + except Exception: + return [template] + + if max_samples and len(results) > max_samples: + results = results[:max_samples] + return results + + +def _strip_domain(intent_id: str) -> str: + """Strip the ``domain:`` prefix from an intent ID to get the base name.""" + return intent_id.split(":", 1)[-1] if ":" in intent_id else intent_id + + +def export_to_locale( + dataset_id: str, + lang: str, + output_dir: str | Path, + *, + split: str = "train", + streaming: bool = True, +) -> int: + """Export templates from a HuggingFace dataset to an OVOS-INTENT-2 locale + directory tree. + + Writes:: + + / + locale/ + / + .intent # all template lines for each intent + .voc # vocabulary expansions + .entity # slot example values + + Parameters + ---------- + dataset_id + Dataset to load (``"hassil-intents"``, ``"intents-for-eval"``, + or ``"massive-templates"``). + lang + Language to export. + output_dir + Root directory for the locale tree. + split + Dataset split. Default ``"train"``. + streaming + Whether to stream from HF. Default True. + + Returns + ------- + int + Number of templates written. + """ + rows = load_dataset_templates( + dataset_id, lang=lang, split=split, streaming=streaming + ) + out = Path(output_dir) + locale = out / "locale" / lang + locale.mkdir(parents=True, exist_ok=True) + + # Collect templates per intent + intent_lines: dict[str, list[str]] = defaultdict(list) + vocabs: dict[str, set[str]] = defaultdict(set) + entities: dict[str, set[str]] = defaultdict(set) + + for row in rows: + name = _strip_domain(row["intent_id"]) + tpl = row["template"] + intent_lines[name].append(tpl) + + # Collect slot examples from row["slots"] + for slot in row.get("slots", []): + slot_name = slot.get("name", "") + for ex in slot.get("examples", []): + entities[slot_name].add(ex) + + # Collect expansions from row["expansions"] + for exp in row.get("expansions", []): + kw = exp.get("keyword", "") + for v in exp.get("values", []): + vocabs[kw].add(v) + + # If no expansions but the template has refs, + # try to extract inline alternations + if not row.get("expansions"): + for m in VOC_RE.finditer(tpl): + kw = m.group(1) + # No expansion data available; carry on + + # Write .intent files + written = 0 + for name, lines in sorted(intent_lines.items()): + intent_path = locale / f"{name}.intent" + with intent_path.open("w", encoding="utf-8") as fh: + for line in lines: + fh.write(line + "\n") + written += len(lines) + + # Write .voc files + for name, values in sorted(vocabs.items()): + if not values: + continue + voc_path = locale / f"{name}.voc" + with voc_path.open("w", encoding="utf-8") as fh: + for v in sorted(values): + fh.write(v + "\n") + + # Write .entity files + for name, values in sorted(entities.items()): + if not values: + continue + entity_path = locale / f"{name}.entity" + with entity_path.open("w", encoding="utf-8") as fh: + for v in sorted(values): + fh.write(v + "\n") + + return written + + +# --------------------------------------------------------------------------- +# CLI +# --------------------------------------------------------------------------- + + +def main(argv: list[str] | None = None) -> int: + import argparse + + parser = argparse.ArgumentParser( + description="Load templates from an OVOS HF dataset and export to locale." + ) + parser.add_argument( + "dataset", + choices=list(SUPPORTED_DATASETS), + help="Dataset to load", + ) + parser.add_argument("lang", help="Language tag (e.g. en, en-US, pt_BR)") + parser.add_argument("output", type=Path, help="Output directory") + parser.add_argument( + "--split", default="train", help="Dataset split (default: train)" + ) + args = parser.parse_args(argv) + + count = export_to_locale(args.dataset, args.lang, args.output, split=args.split) + print( + f"Exported {count} template lines to {args.output}/locale/{args.lang}/" + ) + return 0 + + +if __name__ == "__main__": + raise SystemExit(main()) diff --git a/pyproject.toml b/pyproject.toml index 101172ae..c7963517 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -27,6 +27,9 @@ dependencies = [] langcodes = [ "langcodes", ] +datasets = [ + "datasets", +] test = [ "pytest", "langcodes", diff --git a/test/test_datasets.py b/test/test_datasets.py new file mode 100644 index 00000000..2218a5b7 --- /dev/null +++ b/test/test_datasets.py @@ -0,0 +1,207 @@ +"""Tests for :mod:`ovos_spec_tools.datasets`.""" +from __future__ import annotations + +import json +import tempfile +from pathlib import Path +from unittest.mock import patch, MagicMock + +import pytest + +from ovos_spec_tools.datasets import ( + SUPPORTED_DATASETS, + expand_hf_template, + export_to_locale, + load_dataset_templates, + _normalize_rows, + _resolve_config, + _strip_domain, +) + +# --------------------------------------------------------------------------- +# _resolve_config +# --------------------------------------------------------------------------- + + +class TestResolveConfig: + def test_hassil_intents_plain_lang(self): + assert _resolve_config("hassil-intents", "en") == "en" + assert _resolve_config("hassil-intents", "pt_BR") == "pt_BR" + + def test_intents_for_eval_appends_templates(self): + assert _resolve_config("intents-for-eval", "en-US") == "en-US-templates" + + def test_intents_for_eval_passthrough(self): + assert _resolve_config("intents-for-eval", "en-US-templates") == "en-US-templates" + + def test_massive_templates_appends_templates(self): + assert _resolve_config("massive-templates", "pt-PT") == "pt-PT-templates" + + +# --------------------------------------------------------------------------- +# _strip_domain +# --------------------------------------------------------------------------- + + +class TestStripDomain: + def test_with_domain(self): + assert _strip_domain("homeassistant:hass_turn_on") == "hass_turn_on" + + def test_without_domain(self): + assert _strip_domain("hass_turn_on") == "hass_turn_on" + + def test_multiple_colons(self): + assert _strip_domain("a:b:c") == "b:c" + + +# --------------------------------------------------------------------------- +# _normalize_rows +# --------------------------------------------------------------------------- + + +class TestNormalizeRows: + def test_hassil_intents_style(self): + rows = [ + { + "intent_id": "homeassistant:hass_turn_on", + "domain": "homeassistant", + "template": " {name}", + "slots": [{"name": "name", "examples": ["light"]}], + "expansions": [{"keyword": "turn_on", "values": ["turn on"]}], + } + ] + out = _normalize_rows(rows, "hassil-intents") + assert out[0]["intent_id"] == "homeassistant:hass_turn_on" + assert out[0]["expansions"] == [{"keyword": "turn_on", "values": ["turn on"]}] + + def test_massive_style_fallback(self): + rows = [ + { + "intent": "hass_turn_on", + "template": "(turn on|switch on) {name}", + "slots": [{"name": "name", "examples": ["light"]}], + "text": "turn on the light", + } + ] + out = _normalize_rows(rows, "massive-templates") + assert out[0]["intent_id"] == "hass_turn_on" + assert "expansions" not in out[0] + assert out[0]["text"] == "turn on the light" + + +# --------------------------------------------------------------------------- +# expand_hf_template +# --------------------------------------------------------------------------- + + +class TestExpandHfTemplate: + def test_with_expansions(self): + tpl = " [the] {name}" + exps = [{"keyword": "turn_on", "values": ["turn on", "switch on"]}] + results = expand_hf_template(tpl, exps, max_samples=100) + assert "turn on the {name}" in results + assert "turn on {name}" in results + assert "switch on the {name}" in results + assert len(results) == 4 + + def test_without_expansions_inline(self): + tpl = "(turn on|switch on) [the] {name}" + results = expand_hf_template(tpl, max_samples=100) + assert results == [ + "turn on the {name}", + "switch on the {name}", + "turn on {name}", + "switch on {name}", + ] + + def test_max_samples_cap(self): + tpl = "(a|b|c|d|e) (1|2|3|4|5) (x|y|z)" + results = expand_hf_template(tpl, max_samples=5) + assert len(results) == 5 + + def test_no_grammar_passthrough(self): + tpl = "hello world" + results = expand_hf_template(tpl) + assert results == ["hello world"] + + +# --------------------------------------------------------------------------- +# export_to_locale +# --------------------------------------------------------------------------- + + +class TestExportToLocale: + def test_exports_structure(self): + """Verify that export creates .intent / .voc / .entity files.""" + mock_rows = [ + { + "intent_id": "test:greet", + "template": " {name}", + "slots": [{"name": "name", "examples": ["Alice", "Bob"]}], + "expansions": [{"keyword": "hello", "values": ["hi", "hey"]}], + }, + { + "intent_id": "test:greet", + "template": " [there] {name}", + "slots": [], + "expansions": [{"keyword": "greet", "values": ["hello", "good day"]}], + }, + ] + + with tempfile.TemporaryDirectory() as tmp: + dst = Path(tmp) + + with patch( + "ovos_spec_tools.datasets.load_dataset_templates", + return_value=mock_rows, + ): + count = export_to_locale("hassil-intents", "en", dst) + + locale_en = dst / "locale" / "en" + assert count == 2 + assert (locale_en / "greet.intent").exists() + assert (locale_en / "hello.voc").exists() + assert (locale_en / "greet.voc").exists() + assert (locale_en / "name.entity").exists() + + lines = (locale_en / "name.entity").read_text().splitlines() + assert "Alice" in lines + assert "Bob" in lines + + def test_round_trip_small(self): + """Load a single row, export, verify the intent file has the template.""" + rows = load_dataset_templates("hassil-intents", lang="en", streaming=False) + # Only keep first 3 rows to make it fast + first = [rows[0], rows[1]] + + with tempfile.TemporaryDirectory() as tmp: + dst = Path(tmp) + with patch( + "ovos_spec_tools.datasets.load_dataset_templates", + return_value=first, + ): + count = export_to_locale("hassil-intents", "en", dst) + assert count == 2 + + # Check that first intent file has rows[0] template + name = rows[0]["intent_id"].split(":")[-1] + intent_path = dst / "locale" / "en" / f"{name}.intent" + assert intent_path.exists() + assert rows[0]["template"] in intent_path.read_text() + + +# --------------------------------------------------------------------------- +# SUPPORTED_DATASETS +# --------------------------------------------------------------------------- + + +class TestSupportedDatasets: + def test_contains_three(self): + assert "hassil-intents" in SUPPORTED_DATASETS + assert "intents-for-eval" in SUPPORTED_DATASETS + assert "massive-templates" in SUPPORTED_DATASETS + + def test_urls_valid(self): + for url in SUPPORTED_DATASETS.values(): + assert "/" in url + assert url.startswith("OpenVoiceOS/") From 91d0768ebed7759545ffdc4fdd9ab99ada7e760b Mon Sep 17 00:00:00 2001 From: JarbasAi Date: Tue, 2 Jun 2026 09:31:17 +0100 Subject: [PATCH 02/10] fix: move dataset creation scripts into examples/hass-intent-dataset/ --- examples/{ => hass-intent-dataset}/APPENDIX.md | 0 examples/{ => hass-intent-dataset}/convert_hassil_intents.py | 0 examples/{ => hass-intent-dataset}/export_hf_dataset.py | 0 examples/{ => hass-intent-dataset}/generate_entities.py | 0 examples/{ => hass-intent-dataset}/reexport_recursive.py | 0 examples/{ => hass-intent-dataset}/reexport_uniform.py | 0 6 files changed, 0 insertions(+), 0 deletions(-) rename examples/{ => hass-intent-dataset}/APPENDIX.md (100%) rename examples/{ => hass-intent-dataset}/convert_hassil_intents.py (100%) rename examples/{ => hass-intent-dataset}/export_hf_dataset.py (100%) rename examples/{ => hass-intent-dataset}/generate_entities.py (100%) rename examples/{ => hass-intent-dataset}/reexport_recursive.py (100%) rename examples/{ => hass-intent-dataset}/reexport_uniform.py (100%) diff --git a/examples/APPENDIX.md b/examples/hass-intent-dataset/APPENDIX.md similarity index 100% rename from examples/APPENDIX.md rename to examples/hass-intent-dataset/APPENDIX.md diff --git a/examples/convert_hassil_intents.py b/examples/hass-intent-dataset/convert_hassil_intents.py similarity index 100% rename from examples/convert_hassil_intents.py rename to examples/hass-intent-dataset/convert_hassil_intents.py diff --git a/examples/export_hf_dataset.py b/examples/hass-intent-dataset/export_hf_dataset.py similarity index 100% rename from examples/export_hf_dataset.py rename to examples/hass-intent-dataset/export_hf_dataset.py diff --git a/examples/generate_entities.py b/examples/hass-intent-dataset/generate_entities.py similarity index 100% rename from examples/generate_entities.py rename to examples/hass-intent-dataset/generate_entities.py diff --git a/examples/reexport_recursive.py b/examples/hass-intent-dataset/reexport_recursive.py similarity index 100% rename from examples/reexport_recursive.py rename to examples/hass-intent-dataset/reexport_recursive.py diff --git a/examples/reexport_uniform.py b/examples/hass-intent-dataset/reexport_uniform.py similarity index 100% rename from examples/reexport_uniform.py rename to examples/hass-intent-dataset/reexport_uniform.py From b87c7e3472bcdab8dba679481efcb8cf90cae7f1 Mon Sep 17 00:00:00 2001 From: JarbasAi Date: Tue, 2 Jun 2026 09:32:35 +0100 Subject: [PATCH 03/10] docs: update APPENDIX.md to reflect v3 union semantics and current caps --- examples/hass-intent-dataset/APPENDIX.md | 41 ++++++++++++------------ 1 file changed, 21 insertions(+), 20 deletions(-) diff --git a/examples/hass-intent-dataset/APPENDIX.md b/examples/hass-intent-dataset/APPENDIX.md index 723f38ee..7457785d 100644 --- a/examples/hass-intent-dataset/APPENDIX.md +++ b/examples/hass-intent-dataset/APPENDIX.md @@ -3,7 +3,7 @@ This appendix formalises the mapping between the [hassil] intent grammar (used by Home Assistant's [OHF-Voice/intents] corpus) and the OVOS-INTENT-1 / OVOS-INTENT-2 resource model. It documents the rules -implemented by `examples/convert_hassil_intents.py` and the trade-offs +implemented by `examples/hass-intent-dataset/convert_hassil_intents.py` and the trade-offs the script makes when the two formats disagree. This is **not** a normative OVOS specification. It is a reference for @@ -231,33 +231,32 @@ the canonical parent. ## 6. Sample validation — OVOS-INTENT-1 §3.6 / §5.5 -OVOS-INTENT-1 imposes three constraints hassil does not: +OVOS-INTENT-1 imposes two constraints hassil does not: * **No adjacent slots** — `{a} {b}` is forbidden; a literal word must separate any two slots. Hassil allows it. * **No repeated slot names per sample** — `{a} and {a}` is forbidden. Hassil allows it. - * **Uniform slot signature** — every sample in one `.intent` (and - every phrase in one `.dialog`) must declare the same `{slot}` set. - Hassil allows mixed signatures across data blocks. -The converter materialises the slot-only structure of each template -(literal text replaced with whitespace, slot markers preserved) and -runs the Cartesian enumeration against that. For each rejected -enumeration path the converter falls back to **path-level salvage**: +The third historical constraint — **uniform slot signature** (§5.5) — +was relaxed in OVOS-INTENT-1 v3. `.intent` files now allow templates +with differing slot sets under union semantics. Every valid path is +kept regardless of its signature. + +When a template has some enumerated paths that violate §3.6, the +converter falls back to **path-level salvage**: 1. Enumerate the full template (literal text included). 2. Drop paths that violate §3.6 (adjacency or repeated slots). - 3. Group the survivors by slot signature. - 4. Emit only the group with the maximal signature (matches the - compact-form sig used by sibling samples). + 3. Keep every surviving path regardless of slot signature (v3 + union semantics). If every path is invalid the sample is logged as `all_paths_invalid` and dropped. -Dialog phrase sets recovered from `{% if … %}` branches handle §5.5 -differently — see §8.2 — because dropping a branch means losing one -conditional response, which is worse than emitting an extra file. +Dialog phrase sets recovered from `{% if … %}` branches are exempt from +the union-sig rule — see §8.2 — because dropping a branch means losing +one conditional response, which is worse than emitting an extra file. ## 7. Safety caps @@ -268,7 +267,7 @@ exponential string explosion. The converter guards every stage: |-----|-------|---------| | `MAX_PERM_ELEMS` | 5 | permutations of >5 elements collapse to literal concatenation | | `MAX_SAMPLE_BYTES` | 4 KiB | drop any rewritten template larger than this | -| `MAX_SAMPLE_PATHS` | 2048 | drop samples whose Cartesian expansion exceeds this | +| `MAX_SAMPLE_PATHS` | 20000 | drop samples whose Cartesian expansion exceeds this | | `MAX_ENTITY_VALUES` | 2000 | cap on `range:` list materialisation | | `MAX_RULE_BYTES` | 16 KiB | short-circuit fixed-point rule inlining if a body blows up | | `PROMOTE_RULE_THRESHOLD` | 2 | any rule with ≥1 alt/opt becomes a `.voc` file | @@ -328,7 +327,7 @@ template-to-template converter. ## 9. Audit log Every hard failure is recorded in -`examples/convert_hassil_intents.skipped.tsv` with columns: +`examples/hass-intent-dataset/convert_hassil_intents.skipped.tsv` with columns: lang language code (ISO 639-1 / BCP-47) kind "sample" | "response" | "entity" @@ -353,9 +352,11 @@ The conversion is **lossy** and **one-way**: * Path-salvage emits literal enumerations in place of the original compact template — the `{slot}` placeholders survive but the optional grouping does not. - * §5.5 enforcement picks the maximal-signature group for *samples*, - dropping sub-signature branches; for *dialogs* sub-signature - branches land in `_branch_` companion files instead. + * §5.5 v3 union semantics means samples with differing slot signatures + are kept as separate template lines — they are not grouped or + salvaged into `_branch_` files. For *dialogs*, sub-signature + branches from `{% if %}` decomposition still land in + `_branch_` files (see §8.2). * The canonical-name table folds language-local rule names to English topic names — the reverse direction would need the same table read backwards (one canonical name → multiple local names). From 0fc306baeb85c5097203eaa7b1d62643b90c203b Mon Sep 17 00:00:00 2001 From: JarbasAi Date: Tue, 2 Jun 2026 09:35:03 +0100 Subject: [PATCH 04/10] chore: remove AGENTS.md and TODO.md from PR --- AGENTS.md | 60 ------------------------------------------------------- TODO.md | 15 -------------- 2 files changed, 75 deletions(-) delete mode 100644 AGENTS.md delete mode 100644 TODO.md diff --git a/AGENTS.md b/AGENTS.md deleted file mode 100644 index 03e38594..00000000 --- a/AGENTS.md +++ /dev/null @@ -1,60 +0,0 @@ -# AGENTS.md — ovos-spec-tools - -Reference implementation of the OVOS formal specifications: a dependency-light Python library and `ovos-spec-lint` CLI providing the conformant primitives those specs describe (sentence template expansion, locale resource loading, dialog/prompt rendering, language-tag matching, bus message envelope, and a locale linter). - -## Setup - -```bash -pip install -e . # core, zero runtime dependencies -pip install -e .[langcodes] # adds smart language fallback for LocaleResources / language.py -pip install -e .[test] # pytest + langcodes -``` - -Requires Python 3.10+. - -## Test - -```bash -pytest test -``` - -The `test/` suite covers every module. `langcodes` must be installed (it is in the `test` extra) or language-fallback paths degrade to exact-match. - -## Lint/Typecheck - -`ruff` runs in CI via the shared `lint.yml` workflow. No local ruff config is committed; no typechecker is configured. There is no `.pre-commit-config.yaml`. - -## Layout - -`ovos_spec_tools/` is a flat package, one module per spec primitive: - -- `expansion.py` — OVOS-INTENT-1 sentence template expander (`expand`, `MalformedTemplate`). -- `resources.py` — OVOS-INTENT-2 locale loader (`LocaleResources`), `iter_locale_dirs`, `find_lang_dir`, `keyword_form`, `utterance_contains`, `strip_samples`, resource-file readers, role constants. -- `dialog.py` — OVOS-INTENT-2 §4.2 dialog renderer (`render`, `DialogRenderer`, `UnfilledSlot`). -- `prompt.py` — OVOS-INTENT-2 §4.4 `.prompt` renderer (`render_prompt`, `PromptRenderer`). -- `language.py` — OVOS-INTENT-2 §2.2 language-tag matching (`standardize_lang`, `lang_distance`, `lang_matches`, `closest_lang`); uses `langcodes` when present. -- `message.py` — OVOS-MSG-1 bus message envelope (`Message`, `forward`/`reply`/`response`, `serialize`, `DEFAULT_SESSION_ID`). -- `lint.py` — locale linter (`lint_locale`, `Finding`, `main`); console entry point `ovos-spec-lint`. -- `version.py` — version string (do not edit). - -`test/` mirrors the modules. `examples/` holds runnable scripts plus sample/dirty locale fixtures and a HuggingFace-dataset exporter. `docs/` is a zero-to-hero guide. - -Entry point: console script `ovos-spec-lint` only (`[project.scripts]`). This is **not** an OPM/OVOS plugin or skill — there are no `ovos.plugin.*` or skill entry-point groups. - -## Conventions - -- Branches: `dev` (work) and `master` (stable). NEVER use `main`. -- Never edit `version.py`; gh-automations bumps semver from conventional-commit prefixes (`feat:`, `fix:`, `feat!:`). -- New repos private by default. -- Commit identity: `JarbasAi `. -- Reference `OpenVoiceOS/gh-automations` reusable workflows at `@dev`. -- No Neon / `neon-*` references. -- No meta-commentary (no history, dates, or "design mistake" framing) in docs, commits, code comments, or PRs — describe current state only. -- CI is provided by `OpenVoiceOS/gh-automations`. - -## Gotchas - -- Core has zero runtime dependencies by design; keep it that way. `langcodes` is optional — guard its use so language resolution degrades to exact-match without it, never imports unconditionally. -- `license_check.yml` runs `warn_only: true`: the published PyPI release predates the SPDX classifier so the package self-audits as unknown; do not treat that warning as a regression. -- `lint.py` understands a `--spec-version` (0–3) gate to flag features newer than a target spec; keep role/feature additions wired into that gate. -- Generated artifacts (`build/`, `*.egg-info/`, `.coverage`) appear on disk but are gitignored and untracked — do not commit them. diff --git a/TODO.md b/TODO.md deleted file mode 100644 index dabcbd5b..00000000 --- a/TODO.md +++ /dev/null @@ -1,15 +0,0 @@ -# TODO — ovos-spec-tools - -## Open issues - -- [ ] #2 Dependency Dashboard (Renovate bot) - -## Gaps - -- [ ] `license_check.yml` runs in `warn_only` mode because the PyPI release predates the SPDX license classifier; re-publish so the package self-audits cleanly, then drop `warn_only`. -- [ ] No `coverage_source` enforcement: `coverage.yml` sets `min_coverage: 0`; raise once the suite is judged complete. -- [ ] Build/egg-info/.coverage artifacts present in the working tree (gitignored, untracked) — keep the tree clean before packaging. - -## Code TODOs - -None found. From 2692abdee17ec1665068f40e86482b5d6f4e2f0b Mon Sep 17 00:00:00 2001 From: JarbasAi Date: Tue, 2 Jun 2026 09:35:50 +0100 Subject: [PATCH 05/10] docs: add datasets chapter (7), update README and API reference --- docs/README.md | 11 ++- docs/api-reference.md | 33 +++++++- docs/datasets.md | 188 ++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 227 insertions(+), 5 deletions(-) create mode 100644 docs/datasets.md diff --git a/docs/README.md b/docs/README.md index d8cb6ccb..f6272858 100644 --- a/docs/README.md +++ b/docs/README.md @@ -6,7 +6,7 @@ the small, dependency-light primitives those specs describe, in one place, so OVOS components and third-party tools stop reimplementing them and drifting apart. -It gives you five things: +It gives you six things: - an **expander** — turns a sentence template into the set of sentences it stands for (OVOS-INTENT-1); @@ -15,6 +15,9 @@ It gives you five things: - **language matching** — normalizes tags and finds the closest one; - a **bus message envelope** — the `type` / `data` / `context` JSON contract and its `forward` / `reply` / `response` derivations (OVOS-MSG-1); +- a **dataset loader** — loads, expands, and exports OVOS templates from + HuggingFace datasets (compatible with `hass-intent-templates`, + `intents-for-eval`, `massive-templates`); plus **`ovos-spec-lint`**, a command-line linter for locale folders. @@ -38,9 +41,11 @@ are the foundation everything else rests on. 7. [Bus namespaces](bus-namespaces.md) — the spec topic vocabulary (`SpecMessage`), the legacy↔`ovos.*` `MIGRATION_MAP`, and the transparent dual-emit bridge with its migration window. -8. [Linting](linting.md) — validating a locale folder, from the command line +8. [HuggingFace datasets](datasets.md) — loading templates from HF, expanding + them, and exporting to a locale tree. +9. [Linting](linting.md) — validating a locale folder, from the command line or in CI. -9. [API reference](api-reference.md) — every public name, in brief. +10. [API reference](api-reference.md) — every public name, in brief. ### Proving the scope diff --git a/docs/api-reference.md b/docs/api-reference.md index 699d0bb1..8b35cd5c 100644 --- a/docs/api-reference.md +++ b/docs/api-reference.md @@ -162,13 +162,42 @@ The reserved `"default"` value (OVOS-MSG-1 §4.1) — *the Message originates from the device itself*. An absent `session` on a Message is equivalent for every policy decision. -## Linting — [chapter 7](linting.md) +## Datasets — [chapter 7](datasets.md) + +These functions require the `datasets` extra (`pip install ovos-spec-tools[datasets]`). + +### `SUPPORTED_DATASETS` + +```python +{"hassil-intents": "OpenVoiceOS/hass-intent-templates", + "intents-for-eval": "OpenVoiceOS/intents-for-eval", + "massive-templates": "OpenVoiceOS/massive-templates"} +``` + +### `load_dataset_templates(dataset_id, lang, *, split="train", streaming=True) -> list[dict]` + +Load template rows from a supported HuggingFace dataset. Returns a list of +normalized dicts with keys `intent_id`, `template`, `slots`, and (when the +dataset has it) `expansions`. + +### `expand_hf_template(template, expansions=None, max_samples=2048) -> list[str]` + +Expand a template into concrete utterances. `expansions` is a list of +`{"keyword", "values"}` dicts (as found in the `expansions` column of +hassil-intents). Uses `ovos_spec_tools.expansion.expand` under the hood. + +### `export_to_locale(dataset_id, lang, output_dir, *, split="train", streaming=True) -> int` + +Export templates from a HF dataset into an OVOS-INTENT-2 locale directory at +`/locale//`. Returns the number of template lines written. + +## Linting — [chapter 8](linting.md) ### `lint_locale(path, spec_version=2) -> list[Finding]` Validate every resource file under a locale (or single-language) directory. `spec_version` (0, 1, or 2) flags features newer than that target — see -[chapter 7](linting.md). +[chapter 8](linting.md). ### `Finding` diff --git a/docs/datasets.md b/docs/datasets.md new file mode 100644 index 00000000..4f74cad8 --- /dev/null +++ b/docs/datasets.md @@ -0,0 +1,188 @@ +# 7. HuggingFace datasets + +The `datasets` module loads OVOS-INTENT-2 templates from three HuggingFace +datasets, expands them into concrete utterances, and exports them to a standard +locale directory tree. + +```python +from ovos_spec_tools.datasets import ( + load_dataset_templates, + expand_hf_template, + export_to_locale, + SUPPORTED_DATASETS, +) +``` + +This module requires the optional `datasets` extra: + +```bash +pip install ovos-spec-tools[datasets] +``` + +It is optional because the core library has zero runtime dependencies by design. + +--- + +## 7.1 Supported datasets + +| Short name | HF repo | Configs | Rows per config | Template style | +|------------|---------|---------|-----------------|----------------| +| `hassil-intents` | `OpenVoiceOS/hass-intent-templates` | per-language (`en`, `pt_BR`, …) | ~2–21k | full OVOS syntax with `` refs + `expansions` column | +| `intents-for-eval` | `OpenVoiceOS/intents-for-eval` | `{tag}-templates` (`en-US-templates`, …) | ~1–30k | `` inlined to `(a\|b\|c)` groups | +| `massive-templates` | `OpenVoiceOS/massive-templates` | `{tag}-templates` (`en-US-templates`, …) | ~1–100k | same style as intents-for-eval | + +`SUPPORTED_DATASETS` gives you the mapping: + +```python +SUPPORTED_DATASETS +# {'hassil-intents': 'OpenVoiceOS/hass-intent-templates', +# 'intents-for-eval': 'OpenVoiceOS/intents-for-eval', +# 'massive-templates': 'OpenVoiceOS/massive-templates'} +``` + +--- + +## 7.2 Loading templates + +`load_dataset_templates(dataset_id, lang)` returns a list of normalized row +dicts. Every row has at least `intent_id`, `template`, and `slots`; when the +dataset carries an `expansions` column (hassil-intents only) it is preserved. + +```python +# Load English hassil-intent templates +templates = load_dataset_templates("hassil-intents", lang="en") +len(templates) # 3804 + +templates[0] +# {'intent_id': 'homeassistant:hass_broadcast', +# 'template': '(broadcast|announce) [everywhere] [that] {message}', +# 'slots': [{'name': 'message', 'examples': ['hello', 'dinner is ready']}], +# 'expansions': []} +``` + +Language tags follow the dataset's convention: + +| Dataset | Example `lang` | +|---------|----------------| +| hassil-intents | `en`, `pt_BR`, `zh_CN` | +| intents-for-eval | `en-US`, `pt-PT`, or full config `en-US-templates` | +| massive-templates | same as intents-for-eval | + +--- + +## 7.3 Expanding templates + +`expand_hf_template(template, expansions=None, max_samples=2048)` resolves +`` refs, `(a|b)` alternations, and `[x]` optionals into concrete +utterances using the same engine as `ovos_spec_tools.expansion.expand()`. + +### With expansions (hassil-intents style) + +When the row carries an `expansions` column, pass it in: + +```python +row = templates[1] +# {'template': ' all [[of ](the|my)] timers', +# 'expansions': [{'keyword': 'timer_cancel', 'values': ['cancel', 'stop']}]} + +results = expand_hf_template(row["template"], row["expansions"]) +# ['cancel all of the timers', 'stop all of the timers', +# 'cancel all timers', 'stop all timers', +# 'cancel all the timers'] +``` + +### Without expansions (intents-for-eval style) + +When the template already has `` refs inlined to `(a|b|c)` groups, +omit the expansions argument: + +```python +expand_hf_template("(turn on|switch on) [the] {name}") +# ['turn on the {name}', 'switch on the {name}', +# 'turn on {name}', 'switch on {name}'] +``` + +### Safety cap + +The `max_samples` parameter (default 2048) caps the output list so that a +combinatorial explosion in a template cannot fill memory. + +```python +results = expand_hf_template("(a|b|c|d|e) (1|2|3|4|5) (x|y|z)", + max_samples=5) +len(results) # 5 +``` + +--- + +## 7.4 Exporting to a locale directory + +`export_to_locale(dataset_id, lang, output_dir)` writes `.intent`, `.voc`, +and `.entity` files to `/locale//`. + +```python +export_to_locale("hassil-intents", lang="en", output_dir="/tmp/my-locale") +``` + +Produces: + +``` +/tmp/my-locale/locale/en/ +├── hass_broadcast.intent # template lines for this intent +├── hass_turn_on.intent +├── ... +├── timer_cancel.voc # keyword expansions +├── area.entity # slot example values +├── color.entity +└── ... # 86 files total for English +``` + +### Round-trip workflow + +A typical pipeline loads from HF, inspects, and exports to a locale that a +skill's `LocaleResources` can consume: + +```python +from ovos_spec_tools.datasets import expand_hf_template, export_to_locale +from ovos_spec_tools.resources import LocaleResources + +# 1. Export HF dataset to locale directory +export_to_locale("hassil-intents", lang="en", output_dir="/tmp/locale") + +# 2. Load it with the standard resource loader +resources = LocaleResources(skill_locale="/tmp/locale") + +# 3. Use normally +samples = resources.load_intent("hass_turn_on", "en") +phrases = resources.vocabularies("en") +``` + +--- + +## 7.5 CLI usage + +The module also runs as a command-line tool: + +```bash +# Export English templates +python -m ovos_spec_tools.datasets hassil-intents en /tmp/locale + +# Show expansions for the first 3 templates +python examples/hf_dataset.py hassil-intents en /tmp/locale --expand +``` + +```text +Loading hassil-intents (OpenVoiceOS/hass-intent-templates) / en ... +Loaded 3804 templates + +--- Template expansion samples --- + + [0] intent_id=homeassistant:hass_broadcast + template: (broadcast|announce) [everywhere] [that] {message} + sample utterances (5 total): + - broadcast everywhere that {message} + - announce everywhere that {message} + - broadcast that {message} + - announce that {message} + - broadcast everywhere {message} +``` From 0440d00e9d6d820be9d1d8629e95a3e971b4f1bc Mon Sep 17 00:00:00 2001 From: JarbasAi Date: Tue, 2 Jun 2026 09:47:06 +0100 Subject: [PATCH 06/10] fix: domain-aware {name} slot examples (English only), no English in other langs --- .../hass-intent-dataset/export_hf_dataset.py | 93 +++++++++++++++++-- 1 file changed, 87 insertions(+), 6 deletions(-) diff --git a/examples/hass-intent-dataset/export_hf_dataset.py b/examples/hass-intent-dataset/export_hf_dataset.py index bdd08c55..4dff955f 100644 --- a/examples/hass-intent-dataset/export_hf_dataset.py +++ b/examples/hass-intent-dataset/export_hf_dataset.py @@ -46,6 +46,80 @@ SLOT_RE = re.compile(r"\{([a-zA-Z_][a-zA-Z0-9_]*)\}") VOC_RE = re.compile(r"<([a-zA-Z_][a-zA-Z0-9_]*)>") +# Domain-specific device name examples for the {name} slot +DOMAIN_DEVICE_NAMES: dict[str, list[str]] = { + "light": [ + "kitchen light", "living room light", "bedroom light", + "ceiling light", "lamp", "desk lamp", "floor lamp", + "hallway light", "bathroom light", "overhead light", + ], + "fan": [ + "ceiling fan", "bathroom fan", "kitchen fan", + "stand fan", "desk fan", "exhaust fan", + "living room fan", "bedroom fan", "tower fan", + ], + "cover": [ + "living room blinds", "bedroom curtain", "kitchen blinds", + "garage door", "front gate", "back door", + "roller shutter", "sun shade", "window blind", + ], + "lock": [ + "front door", "back door", "garage door", + "main door", "side door", "patio door", + ], + "climate": [ + "thermostat", "living room thermostat", "bedroom thermostat", + "air conditioner", "heater", "hvac", + ], + "media_player": [ + "tv", "speaker", "living room speaker", + "stereo", "soundbar", "kitchen speaker", + ], + "vacuum": [ + "vacuum", "robot vacuum", "living room vacuum", + "downstairs vacuum", "upstairs vacuum", + ], + "scene": [ + "movie night", "good night", "good morning", + "dinner time", "party mode", "relax", + ], + "script": [ + "good night routine", "morning routine", + "away mode", "arrive home", + ], + "sensor": [ + "temperature sensor", "motion sensor", "door sensor", + "humidity sensor", "window sensor", + ], + "switch": [ + "kitchen switch", "living room outlet", "hallway switch", + "porch light", "christmas lights", + ], + "water_heater": [ + "water heater", "boiler", "hot water tank", + ], + "humidifier": [ + "humidifier", "dehumidifier", "living room humidifier", + "bedroom humidifier", + ], + "homeassistant": [ + "kitchen light", "living room light", "bedroom light", + "front door", "thermostat", "tv", "ceiling fan", + "garage door", "speaker", "vacuum", + ], +} + + +def _extract_domain(intent_name: str) -> str: + """Extract sub-domain from an intent name like ``hass_light_turn_on``. + + Returns the second segment (``light``) or ``"homeassistant"`` as fallback. + """ + parts = intent_name.split("_") + if len(parts) >= 3 and parts[0] == "hass": + return parts[1] + return "homeassistant" + def _extract_slots(template: str) -> list[str]: """Return ordered slot names from a template.""" @@ -82,15 +156,22 @@ def _load_locale_file(path: Path) -> list[str]: def _build_slot_schema( slot_names: list[str], entity_dir: Path, + lang: str = "en", + domain: str | None = None, ) -> list[dict]: """For each slot name, try to find a matching ``.entity`` file and load - example values.""" + example values. For the ``name`` slot, domain-specific device names are + only provided for English — other languages leave ``name`` as a wildcard + with empty examples unless an actual ``.entity`` file exists.""" schema: list[dict] = [] for name in slot_names: - entity_path = entity_dir / f"{name}.entity" examples: list[str] = [] - if entity_path.is_file(): - examples = _load_locale_file(entity_path)[:20] # cap examples + if name == "name" and domain and lang.startswith("en"): + examples = DOMAIN_DEVICE_NAMES.get(domain, [])[:20] + else: + entity_path = entity_dir / f"{name}.entity" + if entity_path.is_file(): + examples = _load_locale_file(entity_path)[:20] schema.append({"name": name, "examples": examples}) return schema @@ -146,11 +227,11 @@ def export_templates( for intent_file in sorted(lang_dir.glob("*.intent")): intent_name = intent_file.stem - # Domain is always "homeassistant" for this corpus + sub_domain = _extract_domain(intent_name) domain = "homeassistant" for template in _load_locale_file(intent_file): slot_names = _extract_slots(template) - slots = _build_slot_schema(slot_names, entity_dir) + slots = _build_slot_schema(slot_names, entity_dir, lang=lang, domain=sub_domain) rows.append( { "intent_id": f"{domain}:{intent_name}", From f804d82801cb0bee38422f1decffc253820b4e4b Mon Sep 17 00:00:00 2001 From: JarbasAi Date: Tue, 2 Jun 2026 10:07:16 +0100 Subject: [PATCH 07/10] feat: base_locale with localized .entity files for all languages MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Drop all hardcoded strings from Python scripts — single source of truth is base_locale//.entity files: - generate_base_locale.py: seed file with translation data for 59 languages - base_locale/: 413 .entity files (area, name, color, state, device_class, domain, floor) across 59 languages - convert_hassil_intents.py: read area.entity from base_locale instead of hardcoded COMMON_AREA_NAMES dict - export_hf_dataset.py: slot examples from base_locale/, drop DOMAIN_DEVICE_NAMES and _extract_domain - generate_entities.py: read from base_locale/ instead of _LANG_OVERRIDES and other hardcoded data Numeric slots (brightness, percentage, temperature, ...) are still generated programmatically as they are language-agnostic. HA internal identifiers (device_class, domain) remain English in all languages. --- .../base_locale/af/area.entity | 12 + .../base_locale/af/color.entity | 11 + .../base_locale/af/device_class.entity | 32 ++ .../base_locale/af/domain.entity | 19 + .../base_locale/af/floor.entity | 5 + .../base_locale/af/name.entity | 10 + .../base_locale/af/state.entity | 6 + .../base_locale/ar/area.entity | 12 + .../base_locale/ar/color.entity | 11 + .../base_locale/ar/device_class.entity | 32 ++ .../base_locale/ar/domain.entity | 19 + .../base_locale/ar/floor.entity | 5 + .../base_locale/ar/name.entity | 10 + .../base_locale/ar/state.entity | 6 + .../base_locale/bg/area.entity | 12 + .../base_locale/bg/color.entity | 11 + .../base_locale/bg/device_class.entity | 32 ++ .../base_locale/bg/domain.entity | 19 + .../base_locale/bg/floor.entity | 5 + .../base_locale/bg/name.entity | 10 + .../base_locale/bg/state.entity | 6 + .../base_locale/bn/area.entity | 12 + .../base_locale/bn/color.entity | 11 + .../base_locale/bn/device_class.entity | 32 ++ .../base_locale/bn/domain.entity | 19 + .../base_locale/bn/floor.entity | 5 + .../base_locale/bn/name.entity | 10 + .../base_locale/bn/state.entity | 6 + .../base_locale/ca/area.entity | 12 + .../base_locale/ca/color.entity | 11 + .../base_locale/ca/device_class.entity | 32 ++ .../base_locale/ca/domain.entity | 19 + .../base_locale/ca/floor.entity | 5 + .../base_locale/ca/name.entity | 10 + .../base_locale/ca/state.entity | 6 + .../base_locale/cs/area.entity | 12 + .../base_locale/cs/color.entity | 11 + .../base_locale/cs/device_class.entity | 32 ++ .../base_locale/cs/domain.entity | 19 + .../base_locale/cs/floor.entity | 5 + .../base_locale/cs/name.entity | 10 + .../base_locale/cs/state.entity | 6 + .../base_locale/cy/area.entity | 12 + .../base_locale/cy/color.entity | 11 + .../base_locale/cy/device_class.entity | 32 ++ .../base_locale/cy/domain.entity | 19 + .../base_locale/cy/floor.entity | 5 + .../base_locale/cy/name.entity | 10 + .../base_locale/cy/state.entity | 6 + .../base_locale/da/area.entity | 12 + .../base_locale/da/color.entity | 11 + .../base_locale/da/device_class.entity | 32 ++ .../base_locale/da/domain.entity | 19 + .../base_locale/da/floor.entity | 5 + .../base_locale/da/name.entity | 10 + .../base_locale/da/state.entity | 6 + .../base_locale/de/area.entity | 12 + .../base_locale/de/color.entity | 11 + .../base_locale/de/device_class.entity | 32 ++ .../base_locale/de/domain.entity | 19 + .../base_locale/de/floor.entity | 5 + .../base_locale/de/name.entity | 10 + .../base_locale/de/state.entity | 6 + .../base_locale/el/area.entity | 12 + .../base_locale/el/color.entity | 11 + .../base_locale/el/device_class.entity | 32 ++ .../base_locale/el/domain.entity | 19 + .../base_locale/el/floor.entity | 5 + .../base_locale/el/name.entity | 10 + .../base_locale/el/state.entity | 6 + .../base_locale/en/area.entity | 12 + .../base_locale/en/color.entity | 11 + .../base_locale/en/device_class.entity | 32 ++ .../base_locale/en/domain.entity | 19 + .../base_locale/en/floor.entity | 5 + .../base_locale/en/name.entity | 10 + .../base_locale/en/state.entity | 6 + .../base_locale/es/area.entity | 12 + .../base_locale/es/color.entity | 11 + .../base_locale/es/device_class.entity | 32 ++ .../base_locale/es/domain.entity | 19 + .../base_locale/es/floor.entity | 5 + .../base_locale/es/name.entity | 10 + .../base_locale/es/state.entity | 6 + .../base_locale/et/area.entity | 12 + .../base_locale/et/color.entity | 11 + .../base_locale/et/device_class.entity | 32 ++ .../base_locale/et/domain.entity | 19 + .../base_locale/et/floor.entity | 5 + .../base_locale/et/name.entity | 10 + .../base_locale/et/state.entity | 6 + .../base_locale/eu/area.entity | 12 + .../base_locale/eu/color.entity | 11 + .../base_locale/eu/device_class.entity | 32 ++ .../base_locale/eu/domain.entity | 19 + .../base_locale/eu/floor.entity | 5 + .../base_locale/eu/name.entity | 10 + .../base_locale/eu/state.entity | 6 + .../base_locale/fa/area.entity | 12 + .../base_locale/fa/color.entity | 11 + .../base_locale/fa/device_class.entity | 32 ++ .../base_locale/fa/domain.entity | 19 + .../base_locale/fa/floor.entity | 5 + .../base_locale/fa/name.entity | 10 + .../base_locale/fa/state.entity | 6 + .../base_locale/fi/area.entity | 12 + .../base_locale/fi/color.entity | 11 + .../base_locale/fi/device_class.entity | 32 ++ .../base_locale/fi/domain.entity | 19 + .../base_locale/fi/floor.entity | 5 + .../base_locale/fi/name.entity | 10 + .../base_locale/fi/state.entity | 6 + .../base_locale/fr/area.entity | 12 + .../base_locale/fr/color.entity | 11 + .../base_locale/fr/device_class.entity | 32 ++ .../base_locale/fr/domain.entity | 19 + .../base_locale/fr/floor.entity | 5 + .../base_locale/fr/name.entity | 10 + .../base_locale/fr/state.entity | 6 + .../base_locale/ga/area.entity | 12 + .../base_locale/ga/color.entity | 11 + .../base_locale/ga/device_class.entity | 32 ++ .../base_locale/ga/domain.entity | 19 + .../base_locale/ga/floor.entity | 5 + .../base_locale/ga/name.entity | 10 + .../base_locale/ga/state.entity | 6 + .../base_locale/gl/area.entity | 12 + .../base_locale/gl/color.entity | 11 + .../base_locale/gl/device_class.entity | 32 ++ .../base_locale/gl/domain.entity | 19 + .../base_locale/gl/floor.entity | 5 + .../base_locale/gl/name.entity | 10 + .../base_locale/gl/state.entity | 6 + .../base_locale/gu/area.entity | 12 + .../base_locale/gu/color.entity | 11 + .../base_locale/gu/device_class.entity | 32 ++ .../base_locale/gu/domain.entity | 19 + .../base_locale/gu/floor.entity | 5 + .../base_locale/gu/name.entity | 10 + .../base_locale/gu/state.entity | 6 + .../base_locale/he/area.entity | 12 + .../base_locale/he/color.entity | 11 + .../base_locale/he/device_class.entity | 32 ++ .../base_locale/he/domain.entity | 19 + .../base_locale/he/floor.entity | 5 + .../base_locale/he/name.entity | 10 + .../base_locale/he/state.entity | 6 + .../base_locale/hi/area.entity | 12 + .../base_locale/hi/color.entity | 11 + .../base_locale/hi/device_class.entity | 32 ++ .../base_locale/hi/domain.entity | 19 + .../base_locale/hi/floor.entity | 5 + .../base_locale/hi/name.entity | 10 + .../base_locale/hi/state.entity | 6 + .../base_locale/hr/area.entity | 12 + .../base_locale/hr/color.entity | 11 + .../base_locale/hr/device_class.entity | 32 ++ .../base_locale/hr/domain.entity | 19 + .../base_locale/hr/floor.entity | 5 + .../base_locale/hr/name.entity | 10 + .../base_locale/hr/state.entity | 6 + .../base_locale/hu/area.entity | 12 + .../base_locale/hu/color.entity | 11 + .../base_locale/hu/device_class.entity | 32 ++ .../base_locale/hu/domain.entity | 19 + .../base_locale/hu/floor.entity | 5 + .../base_locale/hu/name.entity | 10 + .../base_locale/hu/state.entity | 6 + .../base_locale/is/area.entity | 12 + .../base_locale/is/color.entity | 11 + .../base_locale/is/device_class.entity | 32 ++ .../base_locale/is/domain.entity | 19 + .../base_locale/is/floor.entity | 5 + .../base_locale/is/name.entity | 10 + .../base_locale/is/state.entity | 6 + .../base_locale/it/area.entity | 12 + .../base_locale/it/color.entity | 11 + .../base_locale/it/device_class.entity | 32 ++ .../base_locale/it/domain.entity | 19 + .../base_locale/it/floor.entity | 5 + .../base_locale/it/name.entity | 10 + .../base_locale/it/state.entity | 6 + .../base_locale/ja/area.entity | 12 + .../base_locale/ja/color.entity | 11 + .../base_locale/ja/device_class.entity | 32 ++ .../base_locale/ja/domain.entity | 19 + .../base_locale/ja/floor.entity | 5 + .../base_locale/ja/name.entity | 10 + .../base_locale/ja/state.entity | 6 + .../base_locale/ka/area.entity | 12 + .../base_locale/ka/color.entity | 11 + .../base_locale/ka/device_class.entity | 32 ++ .../base_locale/ka/domain.entity | 19 + .../base_locale/ka/floor.entity | 5 + .../base_locale/ka/name.entity | 10 + .../base_locale/ka/state.entity | 6 + .../base_locale/kn/area.entity | 12 + .../base_locale/kn/color.entity | 11 + .../base_locale/kn/device_class.entity | 32 ++ .../base_locale/kn/domain.entity | 19 + .../base_locale/kn/floor.entity | 5 + .../base_locale/kn/name.entity | 10 + .../base_locale/kn/state.entity | 6 + .../base_locale/ko/area.entity | 12 + .../base_locale/ko/color.entity | 11 + .../base_locale/ko/device_class.entity | 32 ++ .../base_locale/ko/domain.entity | 19 + .../base_locale/ko/floor.entity | 5 + .../base_locale/ko/name.entity | 10 + .../base_locale/ko/state.entity | 6 + .../base_locale/kw/area.entity | 12 + .../base_locale/kw/color.entity | 11 + .../base_locale/kw/device_class.entity | 32 ++ .../base_locale/kw/domain.entity | 19 + .../base_locale/kw/floor.entity | 5 + .../base_locale/kw/name.entity | 10 + .../base_locale/kw/state.entity | 6 + .../base_locale/lb/area.entity | 12 + .../base_locale/lb/color.entity | 11 + .../base_locale/lb/device_class.entity | 32 ++ .../base_locale/lb/domain.entity | 19 + .../base_locale/lb/floor.entity | 5 + .../base_locale/lb/name.entity | 10 + .../base_locale/lb/state.entity | 6 + .../base_locale/lt/area.entity | 12 + .../base_locale/lt/color.entity | 11 + .../base_locale/lt/device_class.entity | 32 ++ .../base_locale/lt/domain.entity | 19 + .../base_locale/lt/floor.entity | 5 + .../base_locale/lt/name.entity | 10 + .../base_locale/lt/state.entity | 6 + .../base_locale/lv/area.entity | 12 + .../base_locale/lv/color.entity | 11 + .../base_locale/lv/device_class.entity | 32 ++ .../base_locale/lv/domain.entity | 19 + .../base_locale/lv/floor.entity | 5 + .../base_locale/lv/name.entity | 10 + .../base_locale/lv/state.entity | 6 + .../base_locale/ml/area.entity | 12 + .../base_locale/ml/color.entity | 11 + .../base_locale/ml/device_class.entity | 32 ++ .../base_locale/ml/domain.entity | 19 + .../base_locale/ml/floor.entity | 5 + .../base_locale/ml/name.entity | 10 + .../base_locale/ml/state.entity | 6 + .../base_locale/mn/area.entity | 12 + .../base_locale/mn/color.entity | 11 + .../base_locale/mn/device_class.entity | 32 ++ .../base_locale/mn/domain.entity | 19 + .../base_locale/mn/floor.entity | 5 + .../base_locale/mn/name.entity | 10 + .../base_locale/mn/state.entity | 6 + .../base_locale/mr/area.entity | 12 + .../base_locale/mr/color.entity | 11 + .../base_locale/mr/device_class.entity | 32 ++ .../base_locale/mr/domain.entity | 19 + .../base_locale/mr/floor.entity | 5 + .../base_locale/mr/name.entity | 10 + .../base_locale/mr/state.entity | 6 + .../base_locale/nb/area.entity | 12 + .../base_locale/nb/color.entity | 11 + .../base_locale/nb/device_class.entity | 32 ++ .../base_locale/nb/domain.entity | 19 + .../base_locale/nb/floor.entity | 5 + .../base_locale/nb/name.entity | 10 + .../base_locale/nb/state.entity | 6 + .../base_locale/ne/area.entity | 12 + .../base_locale/ne/color.entity | 11 + .../base_locale/ne/device_class.entity | 32 ++ .../base_locale/ne/domain.entity | 19 + .../base_locale/ne/floor.entity | 5 + .../base_locale/ne/name.entity | 10 + .../base_locale/ne/state.entity | 6 + .../base_locale/nl/area.entity | 12 + .../base_locale/nl/color.entity | 11 + .../base_locale/nl/device_class.entity | 32 ++ .../base_locale/nl/domain.entity | 19 + .../base_locale/nl/floor.entity | 5 + .../base_locale/nl/name.entity | 10 + .../base_locale/nl/state.entity | 6 + .../base_locale/pa/area.entity | 12 + .../base_locale/pa/color.entity | 11 + .../base_locale/pa/device_class.entity | 32 ++ .../base_locale/pa/domain.entity | 19 + .../base_locale/pa/floor.entity | 5 + .../base_locale/pa/name.entity | 10 + .../base_locale/pa/state.entity | 6 + .../base_locale/pl/area.entity | 12 + .../base_locale/pl/color.entity | 11 + .../base_locale/pl/device_class.entity | 32 ++ .../base_locale/pl/domain.entity | 19 + .../base_locale/pl/floor.entity | 5 + .../base_locale/pl/name.entity | 10 + .../base_locale/pl/state.entity | 6 + .../base_locale/pt-BR/area.entity | 12 + .../base_locale/pt-BR/color.entity | 11 + .../base_locale/pt-BR/device_class.entity | 32 ++ .../base_locale/pt-BR/domain.entity | 19 + .../base_locale/pt-BR/floor.entity | 5 + .../base_locale/pt-BR/name.entity | 10 + .../base_locale/pt-BR/state.entity | 6 + .../base_locale/pt/area.entity | 12 + .../base_locale/pt/color.entity | 11 + .../base_locale/pt/device_class.entity | 32 ++ .../base_locale/pt/domain.entity | 19 + .../base_locale/pt/floor.entity | 5 + .../base_locale/pt/name.entity | 10 + .../base_locale/pt/state.entity | 6 + .../base_locale/ro/area.entity | 12 + .../base_locale/ro/color.entity | 11 + .../base_locale/ro/device_class.entity | 32 ++ .../base_locale/ro/domain.entity | 19 + .../base_locale/ro/floor.entity | 5 + .../base_locale/ro/name.entity | 10 + .../base_locale/ro/state.entity | 6 + .../base_locale/ru/area.entity | 12 + .../base_locale/ru/color.entity | 11 + .../base_locale/ru/device_class.entity | 32 ++ .../base_locale/ru/domain.entity | 19 + .../base_locale/ru/floor.entity | 5 + .../base_locale/ru/name.entity | 10 + .../base_locale/ru/state.entity | 6 + .../base_locale/sk/area.entity | 12 + .../base_locale/sk/color.entity | 11 + .../base_locale/sk/device_class.entity | 32 ++ .../base_locale/sk/domain.entity | 19 + .../base_locale/sk/floor.entity | 5 + .../base_locale/sk/name.entity | 10 + .../base_locale/sk/state.entity | 6 + .../base_locale/sl/area.entity | 12 + .../base_locale/sl/color.entity | 11 + .../base_locale/sl/device_class.entity | 32 ++ .../base_locale/sl/domain.entity | 19 + .../base_locale/sl/floor.entity | 5 + .../base_locale/sl/name.entity | 10 + .../base_locale/sl/state.entity | 6 + .../base_locale/sr-Latn/area.entity | 12 + .../base_locale/sr-Latn/color.entity | 11 + .../base_locale/sr-Latn/device_class.entity | 32 ++ .../base_locale/sr-Latn/domain.entity | 19 + .../base_locale/sr-Latn/floor.entity | 5 + .../base_locale/sr-Latn/name.entity | 10 + .../base_locale/sr-Latn/state.entity | 6 + .../base_locale/sr/area.entity | 12 + .../base_locale/sr/color.entity | 11 + .../base_locale/sr/device_class.entity | 32 ++ .../base_locale/sr/domain.entity | 19 + .../base_locale/sr/floor.entity | 5 + .../base_locale/sr/name.entity | 10 + .../base_locale/sr/state.entity | 6 + .../base_locale/sv/area.entity | 12 + .../base_locale/sv/color.entity | 11 + .../base_locale/sv/device_class.entity | 32 ++ .../base_locale/sv/domain.entity | 19 + .../base_locale/sv/floor.entity | 5 + .../base_locale/sv/name.entity | 10 + .../base_locale/sv/state.entity | 6 + .../base_locale/sw/area.entity | 12 + .../base_locale/sw/color.entity | 11 + .../base_locale/sw/device_class.entity | 32 ++ .../base_locale/sw/domain.entity | 19 + .../base_locale/sw/floor.entity | 5 + .../base_locale/sw/name.entity | 10 + .../base_locale/sw/state.entity | 6 + .../base_locale/ta/area.entity | 12 + .../base_locale/ta/color.entity | 11 + .../base_locale/ta/device_class.entity | 32 ++ .../base_locale/ta/domain.entity | 19 + .../base_locale/ta/floor.entity | 5 + .../base_locale/ta/name.entity | 10 + .../base_locale/ta/state.entity | 6 + .../base_locale/te/area.entity | 12 + .../base_locale/te/color.entity | 11 + .../base_locale/te/device_class.entity | 32 ++ .../base_locale/te/domain.entity | 19 + .../base_locale/te/floor.entity | 5 + .../base_locale/te/name.entity | 10 + .../base_locale/te/state.entity | 6 + .../base_locale/uk/area.entity | 12 + .../base_locale/uk/color.entity | 11 + .../base_locale/uk/device_class.entity | 32 ++ .../base_locale/uk/domain.entity | 19 + .../base_locale/uk/floor.entity | 5 + .../base_locale/uk/name.entity | 10 + .../base_locale/uk/state.entity | 6 + .../base_locale/ur/area.entity | 12 + .../base_locale/ur/color.entity | 11 + .../base_locale/ur/device_class.entity | 32 ++ .../base_locale/ur/domain.entity | 19 + .../base_locale/ur/floor.entity | 5 + .../base_locale/ur/name.entity | 10 + .../base_locale/ur/state.entity | 6 + .../base_locale/zh-CN/area.entity | 12 + .../base_locale/zh-CN/color.entity | 11 + .../base_locale/zh-CN/device_class.entity | 32 ++ .../base_locale/zh-CN/domain.entity | 19 + .../base_locale/zh-CN/floor.entity | 5 + .../base_locale/zh-CN/name.entity | 10 + .../base_locale/zh-CN/state.entity | 6 + .../base_locale/zh-HK/area.entity | 12 + .../base_locale/zh-HK/color.entity | 11 + .../base_locale/zh-HK/device_class.entity | 32 ++ .../base_locale/zh-HK/domain.entity | 19 + .../base_locale/zh-HK/floor.entity | 5 + .../base_locale/zh-HK/name.entity | 10 + .../base_locale/zh-HK/state.entity | 6 + .../base_locale/zh-TW/area.entity | 12 + .../base_locale/zh-TW/color.entity | 11 + .../base_locale/zh-TW/device_class.entity | 32 ++ .../base_locale/zh-TW/domain.entity | 19 + .../base_locale/zh-TW/floor.entity | 5 + .../base_locale/zh-TW/name.entity | 10 + .../base_locale/zh-TW/state.entity | 6 + .../convert_hassil_intents.py | 100 +--- .../hass-intent-dataset/export_hf_dataset.py | 120 ++--- .../generate_base_locale.py | 256 ++++++++++ .../hass-intent-dataset/generate_entities.py | 444 +++--------------- 417 files changed, 5972 insertions(+), 553 deletions(-) create mode 100644 examples/hass-intent-dataset/base_locale/af/area.entity create mode 100644 examples/hass-intent-dataset/base_locale/af/color.entity create mode 100644 examples/hass-intent-dataset/base_locale/af/device_class.entity create mode 100644 examples/hass-intent-dataset/base_locale/af/domain.entity create mode 100644 examples/hass-intent-dataset/base_locale/af/floor.entity create mode 100644 examples/hass-intent-dataset/base_locale/af/name.entity create mode 100644 examples/hass-intent-dataset/base_locale/af/state.entity create mode 100644 examples/hass-intent-dataset/base_locale/ar/area.entity create mode 100644 examples/hass-intent-dataset/base_locale/ar/color.entity create mode 100644 examples/hass-intent-dataset/base_locale/ar/device_class.entity create mode 100644 examples/hass-intent-dataset/base_locale/ar/domain.entity create mode 100644 examples/hass-intent-dataset/base_locale/ar/floor.entity create mode 100644 examples/hass-intent-dataset/base_locale/ar/name.entity create mode 100644 examples/hass-intent-dataset/base_locale/ar/state.entity create mode 100644 examples/hass-intent-dataset/base_locale/bg/area.entity create mode 100644 examples/hass-intent-dataset/base_locale/bg/color.entity create mode 100644 examples/hass-intent-dataset/base_locale/bg/device_class.entity create mode 100644 examples/hass-intent-dataset/base_locale/bg/domain.entity create mode 100644 examples/hass-intent-dataset/base_locale/bg/floor.entity create mode 100644 examples/hass-intent-dataset/base_locale/bg/name.entity create mode 100644 examples/hass-intent-dataset/base_locale/bg/state.entity create mode 100644 examples/hass-intent-dataset/base_locale/bn/area.entity create mode 100644 examples/hass-intent-dataset/base_locale/bn/color.entity create mode 100644 examples/hass-intent-dataset/base_locale/bn/device_class.entity create mode 100644 examples/hass-intent-dataset/base_locale/bn/domain.entity create mode 100644 examples/hass-intent-dataset/base_locale/bn/floor.entity create mode 100644 examples/hass-intent-dataset/base_locale/bn/name.entity create mode 100644 examples/hass-intent-dataset/base_locale/bn/state.entity create mode 100644 examples/hass-intent-dataset/base_locale/ca/area.entity create mode 100644 examples/hass-intent-dataset/base_locale/ca/color.entity create mode 100644 examples/hass-intent-dataset/base_locale/ca/device_class.entity create mode 100644 examples/hass-intent-dataset/base_locale/ca/domain.entity create mode 100644 examples/hass-intent-dataset/base_locale/ca/floor.entity create mode 100644 examples/hass-intent-dataset/base_locale/ca/name.entity create mode 100644 examples/hass-intent-dataset/base_locale/ca/state.entity create mode 100644 examples/hass-intent-dataset/base_locale/cs/area.entity create mode 100644 examples/hass-intent-dataset/base_locale/cs/color.entity create mode 100644 examples/hass-intent-dataset/base_locale/cs/device_class.entity create mode 100644 examples/hass-intent-dataset/base_locale/cs/domain.entity create mode 100644 examples/hass-intent-dataset/base_locale/cs/floor.entity create mode 100644 examples/hass-intent-dataset/base_locale/cs/name.entity create mode 100644 examples/hass-intent-dataset/base_locale/cs/state.entity create mode 100644 examples/hass-intent-dataset/base_locale/cy/area.entity create mode 100644 examples/hass-intent-dataset/base_locale/cy/color.entity create mode 100644 examples/hass-intent-dataset/base_locale/cy/device_class.entity create mode 100644 examples/hass-intent-dataset/base_locale/cy/domain.entity create mode 100644 examples/hass-intent-dataset/base_locale/cy/floor.entity create mode 100644 examples/hass-intent-dataset/base_locale/cy/name.entity create mode 100644 examples/hass-intent-dataset/base_locale/cy/state.entity create mode 100644 examples/hass-intent-dataset/base_locale/da/area.entity create mode 100644 examples/hass-intent-dataset/base_locale/da/color.entity create mode 100644 examples/hass-intent-dataset/base_locale/da/device_class.entity create mode 100644 examples/hass-intent-dataset/base_locale/da/domain.entity create mode 100644 examples/hass-intent-dataset/base_locale/da/floor.entity create mode 100644 examples/hass-intent-dataset/base_locale/da/name.entity create mode 100644 examples/hass-intent-dataset/base_locale/da/state.entity create mode 100644 examples/hass-intent-dataset/base_locale/de/area.entity create mode 100644 examples/hass-intent-dataset/base_locale/de/color.entity create mode 100644 examples/hass-intent-dataset/base_locale/de/device_class.entity create mode 100644 examples/hass-intent-dataset/base_locale/de/domain.entity create mode 100644 examples/hass-intent-dataset/base_locale/de/floor.entity create mode 100644 examples/hass-intent-dataset/base_locale/de/name.entity create mode 100644 examples/hass-intent-dataset/base_locale/de/state.entity create mode 100644 examples/hass-intent-dataset/base_locale/el/area.entity create mode 100644 examples/hass-intent-dataset/base_locale/el/color.entity create mode 100644 examples/hass-intent-dataset/base_locale/el/device_class.entity create mode 100644 examples/hass-intent-dataset/base_locale/el/domain.entity create mode 100644 examples/hass-intent-dataset/base_locale/el/floor.entity create mode 100644 examples/hass-intent-dataset/base_locale/el/name.entity create mode 100644 examples/hass-intent-dataset/base_locale/el/state.entity create mode 100644 examples/hass-intent-dataset/base_locale/en/area.entity create mode 100644 examples/hass-intent-dataset/base_locale/en/color.entity create mode 100644 examples/hass-intent-dataset/base_locale/en/device_class.entity create mode 100644 examples/hass-intent-dataset/base_locale/en/domain.entity create mode 100644 examples/hass-intent-dataset/base_locale/en/floor.entity create mode 100644 examples/hass-intent-dataset/base_locale/en/name.entity create mode 100644 examples/hass-intent-dataset/base_locale/en/state.entity create mode 100644 examples/hass-intent-dataset/base_locale/es/area.entity create mode 100644 examples/hass-intent-dataset/base_locale/es/color.entity create mode 100644 examples/hass-intent-dataset/base_locale/es/device_class.entity create mode 100644 examples/hass-intent-dataset/base_locale/es/domain.entity create mode 100644 examples/hass-intent-dataset/base_locale/es/floor.entity create mode 100644 examples/hass-intent-dataset/base_locale/es/name.entity create mode 100644 examples/hass-intent-dataset/base_locale/es/state.entity create mode 100644 examples/hass-intent-dataset/base_locale/et/area.entity create mode 100644 examples/hass-intent-dataset/base_locale/et/color.entity create mode 100644 examples/hass-intent-dataset/base_locale/et/device_class.entity create mode 100644 examples/hass-intent-dataset/base_locale/et/domain.entity create mode 100644 examples/hass-intent-dataset/base_locale/et/floor.entity create mode 100644 examples/hass-intent-dataset/base_locale/et/name.entity create mode 100644 examples/hass-intent-dataset/base_locale/et/state.entity create mode 100644 examples/hass-intent-dataset/base_locale/eu/area.entity create mode 100644 examples/hass-intent-dataset/base_locale/eu/color.entity create mode 100644 examples/hass-intent-dataset/base_locale/eu/device_class.entity create mode 100644 examples/hass-intent-dataset/base_locale/eu/domain.entity create mode 100644 examples/hass-intent-dataset/base_locale/eu/floor.entity create mode 100644 examples/hass-intent-dataset/base_locale/eu/name.entity create mode 100644 examples/hass-intent-dataset/base_locale/eu/state.entity create mode 100644 examples/hass-intent-dataset/base_locale/fa/area.entity create mode 100644 examples/hass-intent-dataset/base_locale/fa/color.entity create mode 100644 examples/hass-intent-dataset/base_locale/fa/device_class.entity create mode 100644 examples/hass-intent-dataset/base_locale/fa/domain.entity create mode 100644 examples/hass-intent-dataset/base_locale/fa/floor.entity create mode 100644 examples/hass-intent-dataset/base_locale/fa/name.entity create mode 100644 examples/hass-intent-dataset/base_locale/fa/state.entity create mode 100644 examples/hass-intent-dataset/base_locale/fi/area.entity create mode 100644 examples/hass-intent-dataset/base_locale/fi/color.entity create mode 100644 examples/hass-intent-dataset/base_locale/fi/device_class.entity create mode 100644 examples/hass-intent-dataset/base_locale/fi/domain.entity create mode 100644 examples/hass-intent-dataset/base_locale/fi/floor.entity create mode 100644 examples/hass-intent-dataset/base_locale/fi/name.entity create mode 100644 examples/hass-intent-dataset/base_locale/fi/state.entity create mode 100644 examples/hass-intent-dataset/base_locale/fr/area.entity create mode 100644 examples/hass-intent-dataset/base_locale/fr/color.entity create mode 100644 examples/hass-intent-dataset/base_locale/fr/device_class.entity create mode 100644 examples/hass-intent-dataset/base_locale/fr/domain.entity create mode 100644 examples/hass-intent-dataset/base_locale/fr/floor.entity create mode 100644 examples/hass-intent-dataset/base_locale/fr/name.entity create mode 100644 examples/hass-intent-dataset/base_locale/fr/state.entity create mode 100644 examples/hass-intent-dataset/base_locale/ga/area.entity create mode 100644 examples/hass-intent-dataset/base_locale/ga/color.entity create mode 100644 examples/hass-intent-dataset/base_locale/ga/device_class.entity create mode 100644 examples/hass-intent-dataset/base_locale/ga/domain.entity create mode 100644 examples/hass-intent-dataset/base_locale/ga/floor.entity create mode 100644 examples/hass-intent-dataset/base_locale/ga/name.entity create mode 100644 examples/hass-intent-dataset/base_locale/ga/state.entity create mode 100644 examples/hass-intent-dataset/base_locale/gl/area.entity create mode 100644 examples/hass-intent-dataset/base_locale/gl/color.entity create mode 100644 examples/hass-intent-dataset/base_locale/gl/device_class.entity create mode 100644 examples/hass-intent-dataset/base_locale/gl/domain.entity create mode 100644 examples/hass-intent-dataset/base_locale/gl/floor.entity create mode 100644 examples/hass-intent-dataset/base_locale/gl/name.entity create mode 100644 examples/hass-intent-dataset/base_locale/gl/state.entity create mode 100644 examples/hass-intent-dataset/base_locale/gu/area.entity create mode 100644 examples/hass-intent-dataset/base_locale/gu/color.entity create mode 100644 examples/hass-intent-dataset/base_locale/gu/device_class.entity create mode 100644 examples/hass-intent-dataset/base_locale/gu/domain.entity create mode 100644 examples/hass-intent-dataset/base_locale/gu/floor.entity create mode 100644 examples/hass-intent-dataset/base_locale/gu/name.entity create mode 100644 examples/hass-intent-dataset/base_locale/gu/state.entity create mode 100644 examples/hass-intent-dataset/base_locale/he/area.entity create mode 100644 examples/hass-intent-dataset/base_locale/he/color.entity create mode 100644 examples/hass-intent-dataset/base_locale/he/device_class.entity create mode 100644 examples/hass-intent-dataset/base_locale/he/domain.entity create mode 100644 examples/hass-intent-dataset/base_locale/he/floor.entity create mode 100644 examples/hass-intent-dataset/base_locale/he/name.entity create mode 100644 examples/hass-intent-dataset/base_locale/he/state.entity create mode 100644 examples/hass-intent-dataset/base_locale/hi/area.entity create mode 100644 examples/hass-intent-dataset/base_locale/hi/color.entity create mode 100644 examples/hass-intent-dataset/base_locale/hi/device_class.entity create mode 100644 examples/hass-intent-dataset/base_locale/hi/domain.entity create mode 100644 examples/hass-intent-dataset/base_locale/hi/floor.entity create mode 100644 examples/hass-intent-dataset/base_locale/hi/name.entity create mode 100644 examples/hass-intent-dataset/base_locale/hi/state.entity create mode 100644 examples/hass-intent-dataset/base_locale/hr/area.entity create mode 100644 examples/hass-intent-dataset/base_locale/hr/color.entity create mode 100644 examples/hass-intent-dataset/base_locale/hr/device_class.entity create mode 100644 examples/hass-intent-dataset/base_locale/hr/domain.entity create mode 100644 examples/hass-intent-dataset/base_locale/hr/floor.entity create mode 100644 examples/hass-intent-dataset/base_locale/hr/name.entity create mode 100644 examples/hass-intent-dataset/base_locale/hr/state.entity create mode 100644 examples/hass-intent-dataset/base_locale/hu/area.entity create mode 100644 examples/hass-intent-dataset/base_locale/hu/color.entity create mode 100644 examples/hass-intent-dataset/base_locale/hu/device_class.entity create mode 100644 examples/hass-intent-dataset/base_locale/hu/domain.entity create mode 100644 examples/hass-intent-dataset/base_locale/hu/floor.entity create mode 100644 examples/hass-intent-dataset/base_locale/hu/name.entity create mode 100644 examples/hass-intent-dataset/base_locale/hu/state.entity create mode 100644 examples/hass-intent-dataset/base_locale/is/area.entity create mode 100644 examples/hass-intent-dataset/base_locale/is/color.entity create mode 100644 examples/hass-intent-dataset/base_locale/is/device_class.entity create mode 100644 examples/hass-intent-dataset/base_locale/is/domain.entity create mode 100644 examples/hass-intent-dataset/base_locale/is/floor.entity create mode 100644 examples/hass-intent-dataset/base_locale/is/name.entity create mode 100644 examples/hass-intent-dataset/base_locale/is/state.entity create mode 100644 examples/hass-intent-dataset/base_locale/it/area.entity create mode 100644 examples/hass-intent-dataset/base_locale/it/color.entity create mode 100644 examples/hass-intent-dataset/base_locale/it/device_class.entity create mode 100644 examples/hass-intent-dataset/base_locale/it/domain.entity create mode 100644 examples/hass-intent-dataset/base_locale/it/floor.entity create mode 100644 examples/hass-intent-dataset/base_locale/it/name.entity create mode 100644 examples/hass-intent-dataset/base_locale/it/state.entity create mode 100644 examples/hass-intent-dataset/base_locale/ja/area.entity create mode 100644 examples/hass-intent-dataset/base_locale/ja/color.entity create mode 100644 examples/hass-intent-dataset/base_locale/ja/device_class.entity create mode 100644 examples/hass-intent-dataset/base_locale/ja/domain.entity create mode 100644 examples/hass-intent-dataset/base_locale/ja/floor.entity create mode 100644 examples/hass-intent-dataset/base_locale/ja/name.entity create mode 100644 examples/hass-intent-dataset/base_locale/ja/state.entity create mode 100644 examples/hass-intent-dataset/base_locale/ka/area.entity create mode 100644 examples/hass-intent-dataset/base_locale/ka/color.entity create mode 100644 examples/hass-intent-dataset/base_locale/ka/device_class.entity create mode 100644 examples/hass-intent-dataset/base_locale/ka/domain.entity create mode 100644 examples/hass-intent-dataset/base_locale/ka/floor.entity create mode 100644 examples/hass-intent-dataset/base_locale/ka/name.entity create mode 100644 examples/hass-intent-dataset/base_locale/ka/state.entity create mode 100644 examples/hass-intent-dataset/base_locale/kn/area.entity create mode 100644 examples/hass-intent-dataset/base_locale/kn/color.entity create mode 100644 examples/hass-intent-dataset/base_locale/kn/device_class.entity create mode 100644 examples/hass-intent-dataset/base_locale/kn/domain.entity create mode 100644 examples/hass-intent-dataset/base_locale/kn/floor.entity create mode 100644 examples/hass-intent-dataset/base_locale/kn/name.entity create mode 100644 examples/hass-intent-dataset/base_locale/kn/state.entity create mode 100644 examples/hass-intent-dataset/base_locale/ko/area.entity create mode 100644 examples/hass-intent-dataset/base_locale/ko/color.entity create mode 100644 examples/hass-intent-dataset/base_locale/ko/device_class.entity create mode 100644 examples/hass-intent-dataset/base_locale/ko/domain.entity create mode 100644 examples/hass-intent-dataset/base_locale/ko/floor.entity create mode 100644 examples/hass-intent-dataset/base_locale/ko/name.entity create mode 100644 examples/hass-intent-dataset/base_locale/ko/state.entity create mode 100644 examples/hass-intent-dataset/base_locale/kw/area.entity create mode 100644 examples/hass-intent-dataset/base_locale/kw/color.entity create mode 100644 examples/hass-intent-dataset/base_locale/kw/device_class.entity create mode 100644 examples/hass-intent-dataset/base_locale/kw/domain.entity create mode 100644 examples/hass-intent-dataset/base_locale/kw/floor.entity create mode 100644 examples/hass-intent-dataset/base_locale/kw/name.entity create mode 100644 examples/hass-intent-dataset/base_locale/kw/state.entity create mode 100644 examples/hass-intent-dataset/base_locale/lb/area.entity create mode 100644 examples/hass-intent-dataset/base_locale/lb/color.entity create mode 100644 examples/hass-intent-dataset/base_locale/lb/device_class.entity create mode 100644 examples/hass-intent-dataset/base_locale/lb/domain.entity create mode 100644 examples/hass-intent-dataset/base_locale/lb/floor.entity create mode 100644 examples/hass-intent-dataset/base_locale/lb/name.entity create mode 100644 examples/hass-intent-dataset/base_locale/lb/state.entity create mode 100644 examples/hass-intent-dataset/base_locale/lt/area.entity create mode 100644 examples/hass-intent-dataset/base_locale/lt/color.entity create mode 100644 examples/hass-intent-dataset/base_locale/lt/device_class.entity create mode 100644 examples/hass-intent-dataset/base_locale/lt/domain.entity create mode 100644 examples/hass-intent-dataset/base_locale/lt/floor.entity create mode 100644 examples/hass-intent-dataset/base_locale/lt/name.entity create mode 100644 examples/hass-intent-dataset/base_locale/lt/state.entity create mode 100644 examples/hass-intent-dataset/base_locale/lv/area.entity create mode 100644 examples/hass-intent-dataset/base_locale/lv/color.entity create mode 100644 examples/hass-intent-dataset/base_locale/lv/device_class.entity create mode 100644 examples/hass-intent-dataset/base_locale/lv/domain.entity create mode 100644 examples/hass-intent-dataset/base_locale/lv/floor.entity create mode 100644 examples/hass-intent-dataset/base_locale/lv/name.entity create mode 100644 examples/hass-intent-dataset/base_locale/lv/state.entity create mode 100644 examples/hass-intent-dataset/base_locale/ml/area.entity create mode 100644 examples/hass-intent-dataset/base_locale/ml/color.entity create mode 100644 examples/hass-intent-dataset/base_locale/ml/device_class.entity create mode 100644 examples/hass-intent-dataset/base_locale/ml/domain.entity create mode 100644 examples/hass-intent-dataset/base_locale/ml/floor.entity create mode 100644 examples/hass-intent-dataset/base_locale/ml/name.entity create mode 100644 examples/hass-intent-dataset/base_locale/ml/state.entity create mode 100644 examples/hass-intent-dataset/base_locale/mn/area.entity create mode 100644 examples/hass-intent-dataset/base_locale/mn/color.entity create mode 100644 examples/hass-intent-dataset/base_locale/mn/device_class.entity create mode 100644 examples/hass-intent-dataset/base_locale/mn/domain.entity create mode 100644 examples/hass-intent-dataset/base_locale/mn/floor.entity create mode 100644 examples/hass-intent-dataset/base_locale/mn/name.entity create mode 100644 examples/hass-intent-dataset/base_locale/mn/state.entity create mode 100644 examples/hass-intent-dataset/base_locale/mr/area.entity create mode 100644 examples/hass-intent-dataset/base_locale/mr/color.entity create mode 100644 examples/hass-intent-dataset/base_locale/mr/device_class.entity create mode 100644 examples/hass-intent-dataset/base_locale/mr/domain.entity create mode 100644 examples/hass-intent-dataset/base_locale/mr/floor.entity create mode 100644 examples/hass-intent-dataset/base_locale/mr/name.entity create mode 100644 examples/hass-intent-dataset/base_locale/mr/state.entity create mode 100644 examples/hass-intent-dataset/base_locale/nb/area.entity create mode 100644 examples/hass-intent-dataset/base_locale/nb/color.entity create mode 100644 examples/hass-intent-dataset/base_locale/nb/device_class.entity create mode 100644 examples/hass-intent-dataset/base_locale/nb/domain.entity create mode 100644 examples/hass-intent-dataset/base_locale/nb/floor.entity create mode 100644 examples/hass-intent-dataset/base_locale/nb/name.entity create mode 100644 examples/hass-intent-dataset/base_locale/nb/state.entity create mode 100644 examples/hass-intent-dataset/base_locale/ne/area.entity create mode 100644 examples/hass-intent-dataset/base_locale/ne/color.entity create mode 100644 examples/hass-intent-dataset/base_locale/ne/device_class.entity create mode 100644 examples/hass-intent-dataset/base_locale/ne/domain.entity create mode 100644 examples/hass-intent-dataset/base_locale/ne/floor.entity create mode 100644 examples/hass-intent-dataset/base_locale/ne/name.entity create mode 100644 examples/hass-intent-dataset/base_locale/ne/state.entity create mode 100644 examples/hass-intent-dataset/base_locale/nl/area.entity create mode 100644 examples/hass-intent-dataset/base_locale/nl/color.entity create mode 100644 examples/hass-intent-dataset/base_locale/nl/device_class.entity create mode 100644 examples/hass-intent-dataset/base_locale/nl/domain.entity create mode 100644 examples/hass-intent-dataset/base_locale/nl/floor.entity create mode 100644 examples/hass-intent-dataset/base_locale/nl/name.entity create mode 100644 examples/hass-intent-dataset/base_locale/nl/state.entity create mode 100644 examples/hass-intent-dataset/base_locale/pa/area.entity create mode 100644 examples/hass-intent-dataset/base_locale/pa/color.entity create mode 100644 examples/hass-intent-dataset/base_locale/pa/device_class.entity create mode 100644 examples/hass-intent-dataset/base_locale/pa/domain.entity create mode 100644 examples/hass-intent-dataset/base_locale/pa/floor.entity create mode 100644 examples/hass-intent-dataset/base_locale/pa/name.entity create mode 100644 examples/hass-intent-dataset/base_locale/pa/state.entity create mode 100644 examples/hass-intent-dataset/base_locale/pl/area.entity create mode 100644 examples/hass-intent-dataset/base_locale/pl/color.entity create mode 100644 examples/hass-intent-dataset/base_locale/pl/device_class.entity create mode 100644 examples/hass-intent-dataset/base_locale/pl/domain.entity create mode 100644 examples/hass-intent-dataset/base_locale/pl/floor.entity create mode 100644 examples/hass-intent-dataset/base_locale/pl/name.entity create mode 100644 examples/hass-intent-dataset/base_locale/pl/state.entity create mode 100644 examples/hass-intent-dataset/base_locale/pt-BR/area.entity create mode 100644 examples/hass-intent-dataset/base_locale/pt-BR/color.entity create mode 100644 examples/hass-intent-dataset/base_locale/pt-BR/device_class.entity create mode 100644 examples/hass-intent-dataset/base_locale/pt-BR/domain.entity create mode 100644 examples/hass-intent-dataset/base_locale/pt-BR/floor.entity create mode 100644 examples/hass-intent-dataset/base_locale/pt-BR/name.entity create mode 100644 examples/hass-intent-dataset/base_locale/pt-BR/state.entity create mode 100644 examples/hass-intent-dataset/base_locale/pt/area.entity create mode 100644 examples/hass-intent-dataset/base_locale/pt/color.entity create mode 100644 examples/hass-intent-dataset/base_locale/pt/device_class.entity create mode 100644 examples/hass-intent-dataset/base_locale/pt/domain.entity create mode 100644 examples/hass-intent-dataset/base_locale/pt/floor.entity create mode 100644 examples/hass-intent-dataset/base_locale/pt/name.entity create mode 100644 examples/hass-intent-dataset/base_locale/pt/state.entity create mode 100644 examples/hass-intent-dataset/base_locale/ro/area.entity create mode 100644 examples/hass-intent-dataset/base_locale/ro/color.entity create mode 100644 examples/hass-intent-dataset/base_locale/ro/device_class.entity create mode 100644 examples/hass-intent-dataset/base_locale/ro/domain.entity create mode 100644 examples/hass-intent-dataset/base_locale/ro/floor.entity create mode 100644 examples/hass-intent-dataset/base_locale/ro/name.entity create mode 100644 examples/hass-intent-dataset/base_locale/ro/state.entity create mode 100644 examples/hass-intent-dataset/base_locale/ru/area.entity create mode 100644 examples/hass-intent-dataset/base_locale/ru/color.entity create mode 100644 examples/hass-intent-dataset/base_locale/ru/device_class.entity create mode 100644 examples/hass-intent-dataset/base_locale/ru/domain.entity create mode 100644 examples/hass-intent-dataset/base_locale/ru/floor.entity create mode 100644 examples/hass-intent-dataset/base_locale/ru/name.entity create mode 100644 examples/hass-intent-dataset/base_locale/ru/state.entity create mode 100644 examples/hass-intent-dataset/base_locale/sk/area.entity create mode 100644 examples/hass-intent-dataset/base_locale/sk/color.entity create mode 100644 examples/hass-intent-dataset/base_locale/sk/device_class.entity create mode 100644 examples/hass-intent-dataset/base_locale/sk/domain.entity create mode 100644 examples/hass-intent-dataset/base_locale/sk/floor.entity create mode 100644 examples/hass-intent-dataset/base_locale/sk/name.entity create mode 100644 examples/hass-intent-dataset/base_locale/sk/state.entity create mode 100644 examples/hass-intent-dataset/base_locale/sl/area.entity create mode 100644 examples/hass-intent-dataset/base_locale/sl/color.entity create mode 100644 examples/hass-intent-dataset/base_locale/sl/device_class.entity create mode 100644 examples/hass-intent-dataset/base_locale/sl/domain.entity create mode 100644 examples/hass-intent-dataset/base_locale/sl/floor.entity create mode 100644 examples/hass-intent-dataset/base_locale/sl/name.entity create mode 100644 examples/hass-intent-dataset/base_locale/sl/state.entity create mode 100644 examples/hass-intent-dataset/base_locale/sr-Latn/area.entity create mode 100644 examples/hass-intent-dataset/base_locale/sr-Latn/color.entity create mode 100644 examples/hass-intent-dataset/base_locale/sr-Latn/device_class.entity create mode 100644 examples/hass-intent-dataset/base_locale/sr-Latn/domain.entity create mode 100644 examples/hass-intent-dataset/base_locale/sr-Latn/floor.entity create mode 100644 examples/hass-intent-dataset/base_locale/sr-Latn/name.entity create mode 100644 examples/hass-intent-dataset/base_locale/sr-Latn/state.entity create mode 100644 examples/hass-intent-dataset/base_locale/sr/area.entity create mode 100644 examples/hass-intent-dataset/base_locale/sr/color.entity create mode 100644 examples/hass-intent-dataset/base_locale/sr/device_class.entity create mode 100644 examples/hass-intent-dataset/base_locale/sr/domain.entity create mode 100644 examples/hass-intent-dataset/base_locale/sr/floor.entity create mode 100644 examples/hass-intent-dataset/base_locale/sr/name.entity create mode 100644 examples/hass-intent-dataset/base_locale/sr/state.entity create mode 100644 examples/hass-intent-dataset/base_locale/sv/area.entity create mode 100644 examples/hass-intent-dataset/base_locale/sv/color.entity create mode 100644 examples/hass-intent-dataset/base_locale/sv/device_class.entity create mode 100644 examples/hass-intent-dataset/base_locale/sv/domain.entity create mode 100644 examples/hass-intent-dataset/base_locale/sv/floor.entity create mode 100644 examples/hass-intent-dataset/base_locale/sv/name.entity create mode 100644 examples/hass-intent-dataset/base_locale/sv/state.entity create mode 100644 examples/hass-intent-dataset/base_locale/sw/area.entity create mode 100644 examples/hass-intent-dataset/base_locale/sw/color.entity create mode 100644 examples/hass-intent-dataset/base_locale/sw/device_class.entity create mode 100644 examples/hass-intent-dataset/base_locale/sw/domain.entity create mode 100644 examples/hass-intent-dataset/base_locale/sw/floor.entity create mode 100644 examples/hass-intent-dataset/base_locale/sw/name.entity create mode 100644 examples/hass-intent-dataset/base_locale/sw/state.entity create mode 100644 examples/hass-intent-dataset/base_locale/ta/area.entity create mode 100644 examples/hass-intent-dataset/base_locale/ta/color.entity create mode 100644 examples/hass-intent-dataset/base_locale/ta/device_class.entity create mode 100644 examples/hass-intent-dataset/base_locale/ta/domain.entity create mode 100644 examples/hass-intent-dataset/base_locale/ta/floor.entity create mode 100644 examples/hass-intent-dataset/base_locale/ta/name.entity create mode 100644 examples/hass-intent-dataset/base_locale/ta/state.entity create mode 100644 examples/hass-intent-dataset/base_locale/te/area.entity create mode 100644 examples/hass-intent-dataset/base_locale/te/color.entity create mode 100644 examples/hass-intent-dataset/base_locale/te/device_class.entity create mode 100644 examples/hass-intent-dataset/base_locale/te/domain.entity create mode 100644 examples/hass-intent-dataset/base_locale/te/floor.entity create mode 100644 examples/hass-intent-dataset/base_locale/te/name.entity create mode 100644 examples/hass-intent-dataset/base_locale/te/state.entity create mode 100644 examples/hass-intent-dataset/base_locale/uk/area.entity create mode 100644 examples/hass-intent-dataset/base_locale/uk/color.entity create mode 100644 examples/hass-intent-dataset/base_locale/uk/device_class.entity create mode 100644 examples/hass-intent-dataset/base_locale/uk/domain.entity create mode 100644 examples/hass-intent-dataset/base_locale/uk/floor.entity create mode 100644 examples/hass-intent-dataset/base_locale/uk/name.entity create mode 100644 examples/hass-intent-dataset/base_locale/uk/state.entity create mode 100644 examples/hass-intent-dataset/base_locale/ur/area.entity create mode 100644 examples/hass-intent-dataset/base_locale/ur/color.entity create mode 100644 examples/hass-intent-dataset/base_locale/ur/device_class.entity create mode 100644 examples/hass-intent-dataset/base_locale/ur/domain.entity create mode 100644 examples/hass-intent-dataset/base_locale/ur/floor.entity create mode 100644 examples/hass-intent-dataset/base_locale/ur/name.entity create mode 100644 examples/hass-intent-dataset/base_locale/ur/state.entity create mode 100644 examples/hass-intent-dataset/base_locale/zh-CN/area.entity create mode 100644 examples/hass-intent-dataset/base_locale/zh-CN/color.entity create mode 100644 examples/hass-intent-dataset/base_locale/zh-CN/device_class.entity create mode 100644 examples/hass-intent-dataset/base_locale/zh-CN/domain.entity create mode 100644 examples/hass-intent-dataset/base_locale/zh-CN/floor.entity create mode 100644 examples/hass-intent-dataset/base_locale/zh-CN/name.entity create mode 100644 examples/hass-intent-dataset/base_locale/zh-CN/state.entity create mode 100644 examples/hass-intent-dataset/base_locale/zh-HK/area.entity create mode 100644 examples/hass-intent-dataset/base_locale/zh-HK/color.entity create mode 100644 examples/hass-intent-dataset/base_locale/zh-HK/device_class.entity create mode 100644 examples/hass-intent-dataset/base_locale/zh-HK/domain.entity create mode 100644 examples/hass-intent-dataset/base_locale/zh-HK/floor.entity create mode 100644 examples/hass-intent-dataset/base_locale/zh-HK/name.entity create mode 100644 examples/hass-intent-dataset/base_locale/zh-HK/state.entity create mode 100644 examples/hass-intent-dataset/base_locale/zh-TW/area.entity create mode 100644 examples/hass-intent-dataset/base_locale/zh-TW/color.entity create mode 100644 examples/hass-intent-dataset/base_locale/zh-TW/device_class.entity create mode 100644 examples/hass-intent-dataset/base_locale/zh-TW/domain.entity create mode 100644 examples/hass-intent-dataset/base_locale/zh-TW/floor.entity create mode 100644 examples/hass-intent-dataset/base_locale/zh-TW/name.entity create mode 100644 examples/hass-intent-dataset/base_locale/zh-TW/state.entity create mode 100644 examples/hass-intent-dataset/generate_base_locale.py diff --git a/examples/hass-intent-dataset/base_locale/af/area.entity b/examples/hass-intent-dataset/base_locale/af/area.entity new file mode 100644 index 00000000..0281e8af --- /dev/null +++ b/examples/hass-intent-dataset/base_locale/af/area.entity @@ -0,0 +1,12 @@ +kombuis +sitkamer +slaapkamer +badkamer +kantoor +motorhuis +gang +kelder +eetkamer +tuin +terras +wasgoedkamer diff --git a/examples/hass-intent-dataset/base_locale/af/color.entity b/examples/hass-intent-dataset/base_locale/af/color.entity new file mode 100644 index 00000000..76285fdf --- /dev/null +++ b/examples/hass-intent-dataset/base_locale/af/color.entity @@ -0,0 +1,11 @@ +white +black +red +orange +yellow +green +blue +purple +brown +pink +turquoise diff --git a/examples/hass-intent-dataset/base_locale/af/device_class.entity b/examples/hass-intent-dataset/base_locale/af/device_class.entity new file mode 100644 index 00000000..8878fb15 --- /dev/null +++ b/examples/hass-intent-dataset/base_locale/af/device_class.entity @@ -0,0 +1,32 @@ +awning +blind +curtain +door +garage +gate +shade +shutter +window +battery +carbon_monoxide +cold +connectivity +gas +heat +light +lock +moisture +motion +occupancy +opening +plug +power +presence +problem +running +safety +smoke +sound +tamper +update +vibration diff --git a/examples/hass-intent-dataset/base_locale/af/domain.entity b/examples/hass-intent-dataset/base_locale/af/domain.entity new file mode 100644 index 00000000..e062292e --- /dev/null +++ b/examples/hass-intent-dataset/base_locale/af/domain.entity @@ -0,0 +1,19 @@ +light +fan +switch +cover +climate +media_player +sensor +binary_sensor +lock +vacuum +timer +input_boolean +scene +script +automation +weather +camera +humidifier +water_heater diff --git a/examples/hass-intent-dataset/base_locale/af/floor.entity b/examples/hass-intent-dataset/base_locale/af/floor.entity new file mode 100644 index 00000000..41f8ddbc --- /dev/null +++ b/examples/hass-intent-dataset/base_locale/af/floor.entity @@ -0,0 +1,5 @@ +ground floor +first floor +second floor +basement +attic diff --git a/examples/hass-intent-dataset/base_locale/af/name.entity b/examples/hass-intent-dataset/base_locale/af/name.entity new file mode 100644 index 00000000..b40f02a9 --- /dev/null +++ b/examples/hass-intent-dataset/base_locale/af/name.entity @@ -0,0 +1,10 @@ +kitchen light +living room light +bedroom light +ceiling fan +thermostat +tv +speaker +front door +garage door +vacuum diff --git a/examples/hass-intent-dataset/base_locale/af/state.entity b/examples/hass-intent-dataset/base_locale/af/state.entity new file mode 100644 index 00000000..4faa6b78 --- /dev/null +++ b/examples/hass-intent-dataset/base_locale/af/state.entity @@ -0,0 +1,6 @@ +on +off +open +closed +locked +unlocked diff --git a/examples/hass-intent-dataset/base_locale/ar/area.entity b/examples/hass-intent-dataset/base_locale/ar/area.entity new file mode 100644 index 00000000..8bb8effa --- /dev/null +++ b/examples/hass-intent-dataset/base_locale/ar/area.entity @@ -0,0 +1,12 @@ +مطبخ +غرفة المعيشة +غرفة النوم +حمام +مكتب +مرآب +ممر +قبو +غرفة الطعام +حديقة +شرفة +غرفة الغسيل diff --git a/examples/hass-intent-dataset/base_locale/ar/color.entity b/examples/hass-intent-dataset/base_locale/ar/color.entity new file mode 100644 index 00000000..bc8bc780 --- /dev/null +++ b/examples/hass-intent-dataset/base_locale/ar/color.entity @@ -0,0 +1,11 @@ +أبيض +أسود +أحمر +برتقالي +أصفر +أخضر +أزرق +بنفسجي +بني +وردي +تركواز diff --git a/examples/hass-intent-dataset/base_locale/ar/device_class.entity b/examples/hass-intent-dataset/base_locale/ar/device_class.entity new file mode 100644 index 00000000..8878fb15 --- /dev/null +++ b/examples/hass-intent-dataset/base_locale/ar/device_class.entity @@ -0,0 +1,32 @@ +awning +blind +curtain +door +garage +gate +shade +shutter +window +battery +carbon_monoxide +cold +connectivity +gas +heat +light +lock +moisture +motion +occupancy +opening +plug +power +presence +problem +running +safety +smoke +sound +tamper +update +vibration diff --git a/examples/hass-intent-dataset/base_locale/ar/domain.entity b/examples/hass-intent-dataset/base_locale/ar/domain.entity new file mode 100644 index 00000000..e062292e --- /dev/null +++ b/examples/hass-intent-dataset/base_locale/ar/domain.entity @@ -0,0 +1,19 @@ +light +fan +switch +cover +climate +media_player +sensor +binary_sensor +lock +vacuum +timer +input_boolean +scene +script +automation +weather +camera +humidifier +water_heater diff --git a/examples/hass-intent-dataset/base_locale/ar/floor.entity b/examples/hass-intent-dataset/base_locale/ar/floor.entity new file mode 100644 index 00000000..781fe339 --- /dev/null +++ b/examples/hass-intent-dataset/base_locale/ar/floor.entity @@ -0,0 +1,5 @@ +الطابق الأرضي +الطابق الأول +الطابق الثاني +القبو +العلية diff --git a/examples/hass-intent-dataset/base_locale/ar/name.entity b/examples/hass-intent-dataset/base_locale/ar/name.entity new file mode 100644 index 00000000..9a5e7cdc --- /dev/null +++ b/examples/hass-intent-dataset/base_locale/ar/name.entity @@ -0,0 +1,10 @@ +ضوء المطبخ +ضوء غرفة المعيشة +ضوء غرفة النوم +مروحة سقف +منظم حرارة +تلفاز +مكبر صوت +الباب الأمامي +باب المرآب +مكنسة diff --git a/examples/hass-intent-dataset/base_locale/ar/state.entity b/examples/hass-intent-dataset/base_locale/ar/state.entity new file mode 100644 index 00000000..7d61e957 --- /dev/null +++ b/examples/hass-intent-dataset/base_locale/ar/state.entity @@ -0,0 +1,6 @@ +مفعل +معطل +مفتوح +مغلق +مقفل +مفتوح diff --git a/examples/hass-intent-dataset/base_locale/bg/area.entity b/examples/hass-intent-dataset/base_locale/bg/area.entity new file mode 100644 index 00000000..aa428b92 --- /dev/null +++ b/examples/hass-intent-dataset/base_locale/bg/area.entity @@ -0,0 +1,12 @@ +кухня +всекидневна +спалня +баня +офис +гараж +коридор +мазе +трапезария +градина +тераса +перално diff --git a/examples/hass-intent-dataset/base_locale/bg/color.entity b/examples/hass-intent-dataset/base_locale/bg/color.entity new file mode 100644 index 00000000..76285fdf --- /dev/null +++ b/examples/hass-intent-dataset/base_locale/bg/color.entity @@ -0,0 +1,11 @@ +white +black +red +orange +yellow +green +blue +purple +brown +pink +turquoise diff --git a/examples/hass-intent-dataset/base_locale/bg/device_class.entity b/examples/hass-intent-dataset/base_locale/bg/device_class.entity new file mode 100644 index 00000000..8878fb15 --- /dev/null +++ b/examples/hass-intent-dataset/base_locale/bg/device_class.entity @@ -0,0 +1,32 @@ +awning +blind +curtain +door +garage +gate +shade +shutter +window +battery +carbon_monoxide +cold +connectivity +gas +heat +light +lock +moisture +motion +occupancy +opening +plug +power +presence +problem +running +safety +smoke +sound +tamper +update +vibration diff --git a/examples/hass-intent-dataset/base_locale/bg/domain.entity b/examples/hass-intent-dataset/base_locale/bg/domain.entity new file mode 100644 index 00000000..e062292e --- /dev/null +++ b/examples/hass-intent-dataset/base_locale/bg/domain.entity @@ -0,0 +1,19 @@ +light +fan +switch +cover +climate +media_player +sensor +binary_sensor +lock +vacuum +timer +input_boolean +scene +script +automation +weather +camera +humidifier +water_heater diff --git a/examples/hass-intent-dataset/base_locale/bg/floor.entity b/examples/hass-intent-dataset/base_locale/bg/floor.entity new file mode 100644 index 00000000..41f8ddbc --- /dev/null +++ b/examples/hass-intent-dataset/base_locale/bg/floor.entity @@ -0,0 +1,5 @@ +ground floor +first floor +second floor +basement +attic diff --git a/examples/hass-intent-dataset/base_locale/bg/name.entity b/examples/hass-intent-dataset/base_locale/bg/name.entity new file mode 100644 index 00000000..b40f02a9 --- /dev/null +++ b/examples/hass-intent-dataset/base_locale/bg/name.entity @@ -0,0 +1,10 @@ +kitchen light +living room light +bedroom light +ceiling fan +thermostat +tv +speaker +front door +garage door +vacuum diff --git a/examples/hass-intent-dataset/base_locale/bg/state.entity b/examples/hass-intent-dataset/base_locale/bg/state.entity new file mode 100644 index 00000000..4faa6b78 --- /dev/null +++ b/examples/hass-intent-dataset/base_locale/bg/state.entity @@ -0,0 +1,6 @@ +on +off +open +closed +locked +unlocked diff --git a/examples/hass-intent-dataset/base_locale/bn/area.entity b/examples/hass-intent-dataset/base_locale/bn/area.entity new file mode 100644 index 00000000..7afc70b9 --- /dev/null +++ b/examples/hass-intent-dataset/base_locale/bn/area.entity @@ -0,0 +1,12 @@ +রান্নাঘর +বসার ঘর +শোবার ঘর +বাথরুম +অফিস +গ্যারেজ +করিডোর +বেসমেন্ট +ডাইনিং রুম +বাগান +বারান্দা +লন্ড্রি রুম diff --git a/examples/hass-intent-dataset/base_locale/bn/color.entity b/examples/hass-intent-dataset/base_locale/bn/color.entity new file mode 100644 index 00000000..76285fdf --- /dev/null +++ b/examples/hass-intent-dataset/base_locale/bn/color.entity @@ -0,0 +1,11 @@ +white +black +red +orange +yellow +green +blue +purple +brown +pink +turquoise diff --git a/examples/hass-intent-dataset/base_locale/bn/device_class.entity b/examples/hass-intent-dataset/base_locale/bn/device_class.entity new file mode 100644 index 00000000..8878fb15 --- /dev/null +++ b/examples/hass-intent-dataset/base_locale/bn/device_class.entity @@ -0,0 +1,32 @@ +awning +blind +curtain +door +garage +gate +shade +shutter +window +battery +carbon_monoxide +cold +connectivity +gas +heat +light +lock +moisture +motion +occupancy +opening +plug +power +presence +problem +running +safety +smoke +sound +tamper +update +vibration diff --git a/examples/hass-intent-dataset/base_locale/bn/domain.entity b/examples/hass-intent-dataset/base_locale/bn/domain.entity new file mode 100644 index 00000000..e062292e --- /dev/null +++ b/examples/hass-intent-dataset/base_locale/bn/domain.entity @@ -0,0 +1,19 @@ +light +fan +switch +cover +climate +media_player +sensor +binary_sensor +lock +vacuum +timer +input_boolean +scene +script +automation +weather +camera +humidifier +water_heater diff --git a/examples/hass-intent-dataset/base_locale/bn/floor.entity b/examples/hass-intent-dataset/base_locale/bn/floor.entity new file mode 100644 index 00000000..41f8ddbc --- /dev/null +++ b/examples/hass-intent-dataset/base_locale/bn/floor.entity @@ -0,0 +1,5 @@ +ground floor +first floor +second floor +basement +attic diff --git a/examples/hass-intent-dataset/base_locale/bn/name.entity b/examples/hass-intent-dataset/base_locale/bn/name.entity new file mode 100644 index 00000000..b40f02a9 --- /dev/null +++ b/examples/hass-intent-dataset/base_locale/bn/name.entity @@ -0,0 +1,10 @@ +kitchen light +living room light +bedroom light +ceiling fan +thermostat +tv +speaker +front door +garage door +vacuum diff --git a/examples/hass-intent-dataset/base_locale/bn/state.entity b/examples/hass-intent-dataset/base_locale/bn/state.entity new file mode 100644 index 00000000..4faa6b78 --- /dev/null +++ b/examples/hass-intent-dataset/base_locale/bn/state.entity @@ -0,0 +1,6 @@ +on +off +open +closed +locked +unlocked diff --git a/examples/hass-intent-dataset/base_locale/ca/area.entity b/examples/hass-intent-dataset/base_locale/ca/area.entity new file mode 100644 index 00000000..599b61db --- /dev/null +++ b/examples/hass-intent-dataset/base_locale/ca/area.entity @@ -0,0 +1,12 @@ +cuina +sala d'estar +dormitori +bany +oficina +garatge +passadís +soterrani +sala de dinar +jardí +terrassa +sala de bugada diff --git a/examples/hass-intent-dataset/base_locale/ca/color.entity b/examples/hass-intent-dataset/base_locale/ca/color.entity new file mode 100644 index 00000000..c1041435 --- /dev/null +++ b/examples/hass-intent-dataset/base_locale/ca/color.entity @@ -0,0 +1,11 @@ +blanc +negre +vermell +taronja +groc +verd +blau +porpra +marró +rosa +turquesa diff --git a/examples/hass-intent-dataset/base_locale/ca/device_class.entity b/examples/hass-intent-dataset/base_locale/ca/device_class.entity new file mode 100644 index 00000000..8878fb15 --- /dev/null +++ b/examples/hass-intent-dataset/base_locale/ca/device_class.entity @@ -0,0 +1,32 @@ +awning +blind +curtain +door +garage +gate +shade +shutter +window +battery +carbon_monoxide +cold +connectivity +gas +heat +light +lock +moisture +motion +occupancy +opening +plug +power +presence +problem +running +safety +smoke +sound +tamper +update +vibration diff --git a/examples/hass-intent-dataset/base_locale/ca/domain.entity b/examples/hass-intent-dataset/base_locale/ca/domain.entity new file mode 100644 index 00000000..e062292e --- /dev/null +++ b/examples/hass-intent-dataset/base_locale/ca/domain.entity @@ -0,0 +1,19 @@ +light +fan +switch +cover +climate +media_player +sensor +binary_sensor +lock +vacuum +timer +input_boolean +scene +script +automation +weather +camera +humidifier +water_heater diff --git a/examples/hass-intent-dataset/base_locale/ca/floor.entity b/examples/hass-intent-dataset/base_locale/ca/floor.entity new file mode 100644 index 00000000..41f8ddbc --- /dev/null +++ b/examples/hass-intent-dataset/base_locale/ca/floor.entity @@ -0,0 +1,5 @@ +ground floor +first floor +second floor +basement +attic diff --git a/examples/hass-intent-dataset/base_locale/ca/name.entity b/examples/hass-intent-dataset/base_locale/ca/name.entity new file mode 100644 index 00000000..b40f02a9 --- /dev/null +++ b/examples/hass-intent-dataset/base_locale/ca/name.entity @@ -0,0 +1,10 @@ +kitchen light +living room light +bedroom light +ceiling fan +thermostat +tv +speaker +front door +garage door +vacuum diff --git a/examples/hass-intent-dataset/base_locale/ca/state.entity b/examples/hass-intent-dataset/base_locale/ca/state.entity new file mode 100644 index 00000000..c353e5f8 --- /dev/null +++ b/examples/hass-intent-dataset/base_locale/ca/state.entity @@ -0,0 +1,6 @@ +encès +apagat +obert +tancat +bloquejat +desbloquejat diff --git a/examples/hass-intent-dataset/base_locale/cs/area.entity b/examples/hass-intent-dataset/base_locale/cs/area.entity new file mode 100644 index 00000000..29e00348 --- /dev/null +++ b/examples/hass-intent-dataset/base_locale/cs/area.entity @@ -0,0 +1,12 @@ +kuchyně +obývací pokoj +ložnice +koupelna +kancelář +garáž +chodba +sklep +jídelna +zahrada +terasa +prádelna diff --git a/examples/hass-intent-dataset/base_locale/cs/color.entity b/examples/hass-intent-dataset/base_locale/cs/color.entity new file mode 100644 index 00000000..76285fdf --- /dev/null +++ b/examples/hass-intent-dataset/base_locale/cs/color.entity @@ -0,0 +1,11 @@ +white +black +red +orange +yellow +green +blue +purple +brown +pink +turquoise diff --git a/examples/hass-intent-dataset/base_locale/cs/device_class.entity b/examples/hass-intent-dataset/base_locale/cs/device_class.entity new file mode 100644 index 00000000..8878fb15 --- /dev/null +++ b/examples/hass-intent-dataset/base_locale/cs/device_class.entity @@ -0,0 +1,32 @@ +awning +blind +curtain +door +garage +gate +shade +shutter +window +battery +carbon_monoxide +cold +connectivity +gas +heat +light +lock +moisture +motion +occupancy +opening +plug +power +presence +problem +running +safety +smoke +sound +tamper +update +vibration diff --git a/examples/hass-intent-dataset/base_locale/cs/domain.entity b/examples/hass-intent-dataset/base_locale/cs/domain.entity new file mode 100644 index 00000000..e062292e --- /dev/null +++ b/examples/hass-intent-dataset/base_locale/cs/domain.entity @@ -0,0 +1,19 @@ +light +fan +switch +cover +climate +media_player +sensor +binary_sensor +lock +vacuum +timer +input_boolean +scene +script +automation +weather +camera +humidifier +water_heater diff --git a/examples/hass-intent-dataset/base_locale/cs/floor.entity b/examples/hass-intent-dataset/base_locale/cs/floor.entity new file mode 100644 index 00000000..41f8ddbc --- /dev/null +++ b/examples/hass-intent-dataset/base_locale/cs/floor.entity @@ -0,0 +1,5 @@ +ground floor +first floor +second floor +basement +attic diff --git a/examples/hass-intent-dataset/base_locale/cs/name.entity b/examples/hass-intent-dataset/base_locale/cs/name.entity new file mode 100644 index 00000000..b40f02a9 --- /dev/null +++ b/examples/hass-intent-dataset/base_locale/cs/name.entity @@ -0,0 +1,10 @@ +kitchen light +living room light +bedroom light +ceiling fan +thermostat +tv +speaker +front door +garage door +vacuum diff --git a/examples/hass-intent-dataset/base_locale/cs/state.entity b/examples/hass-intent-dataset/base_locale/cs/state.entity new file mode 100644 index 00000000..4faa6b78 --- /dev/null +++ b/examples/hass-intent-dataset/base_locale/cs/state.entity @@ -0,0 +1,6 @@ +on +off +open +closed +locked +unlocked diff --git a/examples/hass-intent-dataset/base_locale/cy/area.entity b/examples/hass-intent-dataset/base_locale/cy/area.entity new file mode 100644 index 00000000..97ac35e6 --- /dev/null +++ b/examples/hass-intent-dataset/base_locale/cy/area.entity @@ -0,0 +1,12 @@ +cegin +ystafell fyw +ystafell wely +ystafell molchi +swyddfa +garej +coridor +islor +ystafell fwyta +gardd +teras +ystafell golchi diff --git a/examples/hass-intent-dataset/base_locale/cy/color.entity b/examples/hass-intent-dataset/base_locale/cy/color.entity new file mode 100644 index 00000000..76285fdf --- /dev/null +++ b/examples/hass-intent-dataset/base_locale/cy/color.entity @@ -0,0 +1,11 @@ +white +black +red +orange +yellow +green +blue +purple +brown +pink +turquoise diff --git a/examples/hass-intent-dataset/base_locale/cy/device_class.entity b/examples/hass-intent-dataset/base_locale/cy/device_class.entity new file mode 100644 index 00000000..8878fb15 --- /dev/null +++ b/examples/hass-intent-dataset/base_locale/cy/device_class.entity @@ -0,0 +1,32 @@ +awning +blind +curtain +door +garage +gate +shade +shutter +window +battery +carbon_monoxide +cold +connectivity +gas +heat +light +lock +moisture +motion +occupancy +opening +plug +power +presence +problem +running +safety +smoke +sound +tamper +update +vibration diff --git a/examples/hass-intent-dataset/base_locale/cy/domain.entity b/examples/hass-intent-dataset/base_locale/cy/domain.entity new file mode 100644 index 00000000..e062292e --- /dev/null +++ b/examples/hass-intent-dataset/base_locale/cy/domain.entity @@ -0,0 +1,19 @@ +light +fan +switch +cover +climate +media_player +sensor +binary_sensor +lock +vacuum +timer +input_boolean +scene +script +automation +weather +camera +humidifier +water_heater diff --git a/examples/hass-intent-dataset/base_locale/cy/floor.entity b/examples/hass-intent-dataset/base_locale/cy/floor.entity new file mode 100644 index 00000000..41f8ddbc --- /dev/null +++ b/examples/hass-intent-dataset/base_locale/cy/floor.entity @@ -0,0 +1,5 @@ +ground floor +first floor +second floor +basement +attic diff --git a/examples/hass-intent-dataset/base_locale/cy/name.entity b/examples/hass-intent-dataset/base_locale/cy/name.entity new file mode 100644 index 00000000..b40f02a9 --- /dev/null +++ b/examples/hass-intent-dataset/base_locale/cy/name.entity @@ -0,0 +1,10 @@ +kitchen light +living room light +bedroom light +ceiling fan +thermostat +tv +speaker +front door +garage door +vacuum diff --git a/examples/hass-intent-dataset/base_locale/cy/state.entity b/examples/hass-intent-dataset/base_locale/cy/state.entity new file mode 100644 index 00000000..4faa6b78 --- /dev/null +++ b/examples/hass-intent-dataset/base_locale/cy/state.entity @@ -0,0 +1,6 @@ +on +off +open +closed +locked +unlocked diff --git a/examples/hass-intent-dataset/base_locale/da/area.entity b/examples/hass-intent-dataset/base_locale/da/area.entity new file mode 100644 index 00000000..873fe5bc --- /dev/null +++ b/examples/hass-intent-dataset/base_locale/da/area.entity @@ -0,0 +1,12 @@ +køkken +stue +soveværelse +badeværelse +kontor +garage +gang +kælder +spisestue +have +terrasse +vaskerum diff --git a/examples/hass-intent-dataset/base_locale/da/color.entity b/examples/hass-intent-dataset/base_locale/da/color.entity new file mode 100644 index 00000000..60ee900e --- /dev/null +++ b/examples/hass-intent-dataset/base_locale/da/color.entity @@ -0,0 +1,11 @@ +hvid +sort +rød +orange +gul +grøn +blå +lilla +brun +pink +turkis diff --git a/examples/hass-intent-dataset/base_locale/da/device_class.entity b/examples/hass-intent-dataset/base_locale/da/device_class.entity new file mode 100644 index 00000000..8878fb15 --- /dev/null +++ b/examples/hass-intent-dataset/base_locale/da/device_class.entity @@ -0,0 +1,32 @@ +awning +blind +curtain +door +garage +gate +shade +shutter +window +battery +carbon_monoxide +cold +connectivity +gas +heat +light +lock +moisture +motion +occupancy +opening +plug +power +presence +problem +running +safety +smoke +sound +tamper +update +vibration diff --git a/examples/hass-intent-dataset/base_locale/da/domain.entity b/examples/hass-intent-dataset/base_locale/da/domain.entity new file mode 100644 index 00000000..e062292e --- /dev/null +++ b/examples/hass-intent-dataset/base_locale/da/domain.entity @@ -0,0 +1,19 @@ +light +fan +switch +cover +climate +media_player +sensor +binary_sensor +lock +vacuum +timer +input_boolean +scene +script +automation +weather +camera +humidifier +water_heater diff --git a/examples/hass-intent-dataset/base_locale/da/floor.entity b/examples/hass-intent-dataset/base_locale/da/floor.entity new file mode 100644 index 00000000..41f8ddbc --- /dev/null +++ b/examples/hass-intent-dataset/base_locale/da/floor.entity @@ -0,0 +1,5 @@ +ground floor +first floor +second floor +basement +attic diff --git a/examples/hass-intent-dataset/base_locale/da/name.entity b/examples/hass-intent-dataset/base_locale/da/name.entity new file mode 100644 index 00000000..b40f02a9 --- /dev/null +++ b/examples/hass-intent-dataset/base_locale/da/name.entity @@ -0,0 +1,10 @@ +kitchen light +living room light +bedroom light +ceiling fan +thermostat +tv +speaker +front door +garage door +vacuum diff --git a/examples/hass-intent-dataset/base_locale/da/state.entity b/examples/hass-intent-dataset/base_locale/da/state.entity new file mode 100644 index 00000000..115c08b9 --- /dev/null +++ b/examples/hass-intent-dataset/base_locale/da/state.entity @@ -0,0 +1,6 @@ +til +fra +åben +lukket +låst +oplåst diff --git a/examples/hass-intent-dataset/base_locale/de/area.entity b/examples/hass-intent-dataset/base_locale/de/area.entity new file mode 100644 index 00000000..3d329ecf --- /dev/null +++ b/examples/hass-intent-dataset/base_locale/de/area.entity @@ -0,0 +1,12 @@ +Küche +Wohnzimmer +Schlafzimmer +Badezimmer +Büro +Garage +Flur +Keller +Esszimmer +Garten +Terrasse +Waschküche diff --git a/examples/hass-intent-dataset/base_locale/de/color.entity b/examples/hass-intent-dataset/base_locale/de/color.entity new file mode 100644 index 00000000..3b0dad6e --- /dev/null +++ b/examples/hass-intent-dataset/base_locale/de/color.entity @@ -0,0 +1,11 @@ +weiß +schwarz +rot +orange +gelb +grün +blau +lila +braun +pink +türkis diff --git a/examples/hass-intent-dataset/base_locale/de/device_class.entity b/examples/hass-intent-dataset/base_locale/de/device_class.entity new file mode 100644 index 00000000..8878fb15 --- /dev/null +++ b/examples/hass-intent-dataset/base_locale/de/device_class.entity @@ -0,0 +1,32 @@ +awning +blind +curtain +door +garage +gate +shade +shutter +window +battery +carbon_monoxide +cold +connectivity +gas +heat +light +lock +moisture +motion +occupancy +opening +plug +power +presence +problem +running +safety +smoke +sound +tamper +update +vibration diff --git a/examples/hass-intent-dataset/base_locale/de/domain.entity b/examples/hass-intent-dataset/base_locale/de/domain.entity new file mode 100644 index 00000000..e062292e --- /dev/null +++ b/examples/hass-intent-dataset/base_locale/de/domain.entity @@ -0,0 +1,19 @@ +light +fan +switch +cover +climate +media_player +sensor +binary_sensor +lock +vacuum +timer +input_boolean +scene +script +automation +weather +camera +humidifier +water_heater diff --git a/examples/hass-intent-dataset/base_locale/de/floor.entity b/examples/hass-intent-dataset/base_locale/de/floor.entity new file mode 100644 index 00000000..707dddec --- /dev/null +++ b/examples/hass-intent-dataset/base_locale/de/floor.entity @@ -0,0 +1,5 @@ +Erdgeschoss +erster Stock +zweiter Stock +Keller +Dachboden diff --git a/examples/hass-intent-dataset/base_locale/de/name.entity b/examples/hass-intent-dataset/base_locale/de/name.entity new file mode 100644 index 00000000..a000b48f --- /dev/null +++ b/examples/hass-intent-dataset/base_locale/de/name.entity @@ -0,0 +1,10 @@ +Küchenlicht +Wohnzimmerlicht +Schlafzimmerlicht +Deckenventilator +Thermostat +Fernseher +Lautsprecher +Eingangstür +Garagentor +Staubsauger diff --git a/examples/hass-intent-dataset/base_locale/de/state.entity b/examples/hass-intent-dataset/base_locale/de/state.entity new file mode 100644 index 00000000..5f2c86d4 --- /dev/null +++ b/examples/hass-intent-dataset/base_locale/de/state.entity @@ -0,0 +1,6 @@ +an +aus +offen +geschlossen +verriegelt +entriegelt diff --git a/examples/hass-intent-dataset/base_locale/el/area.entity b/examples/hass-intent-dataset/base_locale/el/area.entity new file mode 100644 index 00000000..e04c57fa --- /dev/null +++ b/examples/hass-intent-dataset/base_locale/el/area.entity @@ -0,0 +1,12 @@ +κουζίνα +σαλόνι +υπνοδωμάτιο +μπάνιο +γραφείο +γκαράζ +διάδρομος +υπόγειο +τραπεζαρία +κήπος +βεράντα +πλυντήριο diff --git a/examples/hass-intent-dataset/base_locale/el/color.entity b/examples/hass-intent-dataset/base_locale/el/color.entity new file mode 100644 index 00000000..76285fdf --- /dev/null +++ b/examples/hass-intent-dataset/base_locale/el/color.entity @@ -0,0 +1,11 @@ +white +black +red +orange +yellow +green +blue +purple +brown +pink +turquoise diff --git a/examples/hass-intent-dataset/base_locale/el/device_class.entity b/examples/hass-intent-dataset/base_locale/el/device_class.entity new file mode 100644 index 00000000..8878fb15 --- /dev/null +++ b/examples/hass-intent-dataset/base_locale/el/device_class.entity @@ -0,0 +1,32 @@ +awning +blind +curtain +door +garage +gate +shade +shutter +window +battery +carbon_monoxide +cold +connectivity +gas +heat +light +lock +moisture +motion +occupancy +opening +plug +power +presence +problem +running +safety +smoke +sound +tamper +update +vibration diff --git a/examples/hass-intent-dataset/base_locale/el/domain.entity b/examples/hass-intent-dataset/base_locale/el/domain.entity new file mode 100644 index 00000000..e062292e --- /dev/null +++ b/examples/hass-intent-dataset/base_locale/el/domain.entity @@ -0,0 +1,19 @@ +light +fan +switch +cover +climate +media_player +sensor +binary_sensor +lock +vacuum +timer +input_boolean +scene +script +automation +weather +camera +humidifier +water_heater diff --git a/examples/hass-intent-dataset/base_locale/el/floor.entity b/examples/hass-intent-dataset/base_locale/el/floor.entity new file mode 100644 index 00000000..41f8ddbc --- /dev/null +++ b/examples/hass-intent-dataset/base_locale/el/floor.entity @@ -0,0 +1,5 @@ +ground floor +first floor +second floor +basement +attic diff --git a/examples/hass-intent-dataset/base_locale/el/name.entity b/examples/hass-intent-dataset/base_locale/el/name.entity new file mode 100644 index 00000000..b40f02a9 --- /dev/null +++ b/examples/hass-intent-dataset/base_locale/el/name.entity @@ -0,0 +1,10 @@ +kitchen light +living room light +bedroom light +ceiling fan +thermostat +tv +speaker +front door +garage door +vacuum diff --git a/examples/hass-intent-dataset/base_locale/el/state.entity b/examples/hass-intent-dataset/base_locale/el/state.entity new file mode 100644 index 00000000..4faa6b78 --- /dev/null +++ b/examples/hass-intent-dataset/base_locale/el/state.entity @@ -0,0 +1,6 @@ +on +off +open +closed +locked +unlocked diff --git a/examples/hass-intent-dataset/base_locale/en/area.entity b/examples/hass-intent-dataset/base_locale/en/area.entity new file mode 100644 index 00000000..29357647 --- /dev/null +++ b/examples/hass-intent-dataset/base_locale/en/area.entity @@ -0,0 +1,12 @@ +kitchen +living room +bedroom +bathroom +office +garage +hallway +basement +dining room +garden +terrace +laundry room diff --git a/examples/hass-intent-dataset/base_locale/en/color.entity b/examples/hass-intent-dataset/base_locale/en/color.entity new file mode 100644 index 00000000..76285fdf --- /dev/null +++ b/examples/hass-intent-dataset/base_locale/en/color.entity @@ -0,0 +1,11 @@ +white +black +red +orange +yellow +green +blue +purple +brown +pink +turquoise diff --git a/examples/hass-intent-dataset/base_locale/en/device_class.entity b/examples/hass-intent-dataset/base_locale/en/device_class.entity new file mode 100644 index 00000000..8878fb15 --- /dev/null +++ b/examples/hass-intent-dataset/base_locale/en/device_class.entity @@ -0,0 +1,32 @@ +awning +blind +curtain +door +garage +gate +shade +shutter +window +battery +carbon_monoxide +cold +connectivity +gas +heat +light +lock +moisture +motion +occupancy +opening +plug +power +presence +problem +running +safety +smoke +sound +tamper +update +vibration diff --git a/examples/hass-intent-dataset/base_locale/en/domain.entity b/examples/hass-intent-dataset/base_locale/en/domain.entity new file mode 100644 index 00000000..e062292e --- /dev/null +++ b/examples/hass-intent-dataset/base_locale/en/domain.entity @@ -0,0 +1,19 @@ +light +fan +switch +cover +climate +media_player +sensor +binary_sensor +lock +vacuum +timer +input_boolean +scene +script +automation +weather +camera +humidifier +water_heater diff --git a/examples/hass-intent-dataset/base_locale/en/floor.entity b/examples/hass-intent-dataset/base_locale/en/floor.entity new file mode 100644 index 00000000..41f8ddbc --- /dev/null +++ b/examples/hass-intent-dataset/base_locale/en/floor.entity @@ -0,0 +1,5 @@ +ground floor +first floor +second floor +basement +attic diff --git a/examples/hass-intent-dataset/base_locale/en/name.entity b/examples/hass-intent-dataset/base_locale/en/name.entity new file mode 100644 index 00000000..b40f02a9 --- /dev/null +++ b/examples/hass-intent-dataset/base_locale/en/name.entity @@ -0,0 +1,10 @@ +kitchen light +living room light +bedroom light +ceiling fan +thermostat +tv +speaker +front door +garage door +vacuum diff --git a/examples/hass-intent-dataset/base_locale/en/state.entity b/examples/hass-intent-dataset/base_locale/en/state.entity new file mode 100644 index 00000000..4faa6b78 --- /dev/null +++ b/examples/hass-intent-dataset/base_locale/en/state.entity @@ -0,0 +1,6 @@ +on +off +open +closed +locked +unlocked diff --git a/examples/hass-intent-dataset/base_locale/es/area.entity b/examples/hass-intent-dataset/base_locale/es/area.entity new file mode 100644 index 00000000..71c7f115 --- /dev/null +++ b/examples/hass-intent-dataset/base_locale/es/area.entity @@ -0,0 +1,12 @@ +cocina +salón +dormitorio +baño +oficina +garaje +pasillo +sótano +comedor +jardín +terraza +lavandería diff --git a/examples/hass-intent-dataset/base_locale/es/color.entity b/examples/hass-intent-dataset/base_locale/es/color.entity new file mode 100644 index 00000000..398506fe --- /dev/null +++ b/examples/hass-intent-dataset/base_locale/es/color.entity @@ -0,0 +1,11 @@ +blanco +negro +rojo +naranja +amarillo +verde +azul +púrpura +marrón +rosa +turquesa diff --git a/examples/hass-intent-dataset/base_locale/es/device_class.entity b/examples/hass-intent-dataset/base_locale/es/device_class.entity new file mode 100644 index 00000000..8878fb15 --- /dev/null +++ b/examples/hass-intent-dataset/base_locale/es/device_class.entity @@ -0,0 +1,32 @@ +awning +blind +curtain +door +garage +gate +shade +shutter +window +battery +carbon_monoxide +cold +connectivity +gas +heat +light +lock +moisture +motion +occupancy +opening +plug +power +presence +problem +running +safety +smoke +sound +tamper +update +vibration diff --git a/examples/hass-intent-dataset/base_locale/es/domain.entity b/examples/hass-intent-dataset/base_locale/es/domain.entity new file mode 100644 index 00000000..e062292e --- /dev/null +++ b/examples/hass-intent-dataset/base_locale/es/domain.entity @@ -0,0 +1,19 @@ +light +fan +switch +cover +climate +media_player +sensor +binary_sensor +lock +vacuum +timer +input_boolean +scene +script +automation +weather +camera +humidifier +water_heater diff --git a/examples/hass-intent-dataset/base_locale/es/floor.entity b/examples/hass-intent-dataset/base_locale/es/floor.entity new file mode 100644 index 00000000..e73b32fd --- /dev/null +++ b/examples/hass-intent-dataset/base_locale/es/floor.entity @@ -0,0 +1,5 @@ +planta baja +primer piso +segundo piso +sótano +ático diff --git a/examples/hass-intent-dataset/base_locale/es/name.entity b/examples/hass-intent-dataset/base_locale/es/name.entity new file mode 100644 index 00000000..88538b35 --- /dev/null +++ b/examples/hass-intent-dataset/base_locale/es/name.entity @@ -0,0 +1,10 @@ +luz de la cocina +luz del salón +luz del dormitorio +ventilador de techo +termostato +televisor +altavoz +puerta principal +puerta del garaje +aspiradora diff --git a/examples/hass-intent-dataset/base_locale/es/state.entity b/examples/hass-intent-dataset/base_locale/es/state.entity new file mode 100644 index 00000000..0dbef9d8 --- /dev/null +++ b/examples/hass-intent-dataset/base_locale/es/state.entity @@ -0,0 +1,6 @@ +encendido +apagado +abierto +cerrado +bloqueado +desbloqueado diff --git a/examples/hass-intent-dataset/base_locale/et/area.entity b/examples/hass-intent-dataset/base_locale/et/area.entity new file mode 100644 index 00000000..b9b2ff7e --- /dev/null +++ b/examples/hass-intent-dataset/base_locale/et/area.entity @@ -0,0 +1,12 @@ +köök +elutuba +magamistuba +vannituba +kontor +garaaž +esik +kelder +söögituba +aed +terrass +pesuköök diff --git a/examples/hass-intent-dataset/base_locale/et/color.entity b/examples/hass-intent-dataset/base_locale/et/color.entity new file mode 100644 index 00000000..76285fdf --- /dev/null +++ b/examples/hass-intent-dataset/base_locale/et/color.entity @@ -0,0 +1,11 @@ +white +black +red +orange +yellow +green +blue +purple +brown +pink +turquoise diff --git a/examples/hass-intent-dataset/base_locale/et/device_class.entity b/examples/hass-intent-dataset/base_locale/et/device_class.entity new file mode 100644 index 00000000..8878fb15 --- /dev/null +++ b/examples/hass-intent-dataset/base_locale/et/device_class.entity @@ -0,0 +1,32 @@ +awning +blind +curtain +door +garage +gate +shade +shutter +window +battery +carbon_monoxide +cold +connectivity +gas +heat +light +lock +moisture +motion +occupancy +opening +plug +power +presence +problem +running +safety +smoke +sound +tamper +update +vibration diff --git a/examples/hass-intent-dataset/base_locale/et/domain.entity b/examples/hass-intent-dataset/base_locale/et/domain.entity new file mode 100644 index 00000000..e062292e --- /dev/null +++ b/examples/hass-intent-dataset/base_locale/et/domain.entity @@ -0,0 +1,19 @@ +light +fan +switch +cover +climate +media_player +sensor +binary_sensor +lock +vacuum +timer +input_boolean +scene +script +automation +weather +camera +humidifier +water_heater diff --git a/examples/hass-intent-dataset/base_locale/et/floor.entity b/examples/hass-intent-dataset/base_locale/et/floor.entity new file mode 100644 index 00000000..41f8ddbc --- /dev/null +++ b/examples/hass-intent-dataset/base_locale/et/floor.entity @@ -0,0 +1,5 @@ +ground floor +first floor +second floor +basement +attic diff --git a/examples/hass-intent-dataset/base_locale/et/name.entity b/examples/hass-intent-dataset/base_locale/et/name.entity new file mode 100644 index 00000000..b40f02a9 --- /dev/null +++ b/examples/hass-intent-dataset/base_locale/et/name.entity @@ -0,0 +1,10 @@ +kitchen light +living room light +bedroom light +ceiling fan +thermostat +tv +speaker +front door +garage door +vacuum diff --git a/examples/hass-intent-dataset/base_locale/et/state.entity b/examples/hass-intent-dataset/base_locale/et/state.entity new file mode 100644 index 00000000..4faa6b78 --- /dev/null +++ b/examples/hass-intent-dataset/base_locale/et/state.entity @@ -0,0 +1,6 @@ +on +off +open +closed +locked +unlocked diff --git a/examples/hass-intent-dataset/base_locale/eu/area.entity b/examples/hass-intent-dataset/base_locale/eu/area.entity new file mode 100644 index 00000000..be7c3a0e --- /dev/null +++ b/examples/hass-intent-dataset/base_locale/eu/area.entity @@ -0,0 +1,12 @@ +sukaldea +egongela +logela +bainugela +bulegoa +garajea +korridorea +sotoa +jangela +lorategia +terraza +garbitegia diff --git a/examples/hass-intent-dataset/base_locale/eu/color.entity b/examples/hass-intent-dataset/base_locale/eu/color.entity new file mode 100644 index 00000000..76285fdf --- /dev/null +++ b/examples/hass-intent-dataset/base_locale/eu/color.entity @@ -0,0 +1,11 @@ +white +black +red +orange +yellow +green +blue +purple +brown +pink +turquoise diff --git a/examples/hass-intent-dataset/base_locale/eu/device_class.entity b/examples/hass-intent-dataset/base_locale/eu/device_class.entity new file mode 100644 index 00000000..8878fb15 --- /dev/null +++ b/examples/hass-intent-dataset/base_locale/eu/device_class.entity @@ -0,0 +1,32 @@ +awning +blind +curtain +door +garage +gate +shade +shutter +window +battery +carbon_monoxide +cold +connectivity +gas +heat +light +lock +moisture +motion +occupancy +opening +plug +power +presence +problem +running +safety +smoke +sound +tamper +update +vibration diff --git a/examples/hass-intent-dataset/base_locale/eu/domain.entity b/examples/hass-intent-dataset/base_locale/eu/domain.entity new file mode 100644 index 00000000..e062292e --- /dev/null +++ b/examples/hass-intent-dataset/base_locale/eu/domain.entity @@ -0,0 +1,19 @@ +light +fan +switch +cover +climate +media_player +sensor +binary_sensor +lock +vacuum +timer +input_boolean +scene +script +automation +weather +camera +humidifier +water_heater diff --git a/examples/hass-intent-dataset/base_locale/eu/floor.entity b/examples/hass-intent-dataset/base_locale/eu/floor.entity new file mode 100644 index 00000000..41f8ddbc --- /dev/null +++ b/examples/hass-intent-dataset/base_locale/eu/floor.entity @@ -0,0 +1,5 @@ +ground floor +first floor +second floor +basement +attic diff --git a/examples/hass-intent-dataset/base_locale/eu/name.entity b/examples/hass-intent-dataset/base_locale/eu/name.entity new file mode 100644 index 00000000..b40f02a9 --- /dev/null +++ b/examples/hass-intent-dataset/base_locale/eu/name.entity @@ -0,0 +1,10 @@ +kitchen light +living room light +bedroom light +ceiling fan +thermostat +tv +speaker +front door +garage door +vacuum diff --git a/examples/hass-intent-dataset/base_locale/eu/state.entity b/examples/hass-intent-dataset/base_locale/eu/state.entity new file mode 100644 index 00000000..4faa6b78 --- /dev/null +++ b/examples/hass-intent-dataset/base_locale/eu/state.entity @@ -0,0 +1,6 @@ +on +off +open +closed +locked +unlocked diff --git a/examples/hass-intent-dataset/base_locale/fa/area.entity b/examples/hass-intent-dataset/base_locale/fa/area.entity new file mode 100644 index 00000000..41e18acc --- /dev/null +++ b/examples/hass-intent-dataset/base_locale/fa/area.entity @@ -0,0 +1,12 @@ +آشپزخانه +اتاق نشیمن +اتاق خواب +حمام +دفتر +گاراژ +راهرو +زیرزمین +اتاق ناهارخوری +باغ +تراس +اتاق لباسشویی diff --git a/examples/hass-intent-dataset/base_locale/fa/color.entity b/examples/hass-intent-dataset/base_locale/fa/color.entity new file mode 100644 index 00000000..76285fdf --- /dev/null +++ b/examples/hass-intent-dataset/base_locale/fa/color.entity @@ -0,0 +1,11 @@ +white +black +red +orange +yellow +green +blue +purple +brown +pink +turquoise diff --git a/examples/hass-intent-dataset/base_locale/fa/device_class.entity b/examples/hass-intent-dataset/base_locale/fa/device_class.entity new file mode 100644 index 00000000..8878fb15 --- /dev/null +++ b/examples/hass-intent-dataset/base_locale/fa/device_class.entity @@ -0,0 +1,32 @@ +awning +blind +curtain +door +garage +gate +shade +shutter +window +battery +carbon_monoxide +cold +connectivity +gas +heat +light +lock +moisture +motion +occupancy +opening +plug +power +presence +problem +running +safety +smoke +sound +tamper +update +vibration diff --git a/examples/hass-intent-dataset/base_locale/fa/domain.entity b/examples/hass-intent-dataset/base_locale/fa/domain.entity new file mode 100644 index 00000000..e062292e --- /dev/null +++ b/examples/hass-intent-dataset/base_locale/fa/domain.entity @@ -0,0 +1,19 @@ +light +fan +switch +cover +climate +media_player +sensor +binary_sensor +lock +vacuum +timer +input_boolean +scene +script +automation +weather +camera +humidifier +water_heater diff --git a/examples/hass-intent-dataset/base_locale/fa/floor.entity b/examples/hass-intent-dataset/base_locale/fa/floor.entity new file mode 100644 index 00000000..41f8ddbc --- /dev/null +++ b/examples/hass-intent-dataset/base_locale/fa/floor.entity @@ -0,0 +1,5 @@ +ground floor +first floor +second floor +basement +attic diff --git a/examples/hass-intent-dataset/base_locale/fa/name.entity b/examples/hass-intent-dataset/base_locale/fa/name.entity new file mode 100644 index 00000000..b40f02a9 --- /dev/null +++ b/examples/hass-intent-dataset/base_locale/fa/name.entity @@ -0,0 +1,10 @@ +kitchen light +living room light +bedroom light +ceiling fan +thermostat +tv +speaker +front door +garage door +vacuum diff --git a/examples/hass-intent-dataset/base_locale/fa/state.entity b/examples/hass-intent-dataset/base_locale/fa/state.entity new file mode 100644 index 00000000..4faa6b78 --- /dev/null +++ b/examples/hass-intent-dataset/base_locale/fa/state.entity @@ -0,0 +1,6 @@ +on +off +open +closed +locked +unlocked diff --git a/examples/hass-intent-dataset/base_locale/fi/area.entity b/examples/hass-intent-dataset/base_locale/fi/area.entity new file mode 100644 index 00000000..176d99bc --- /dev/null +++ b/examples/hass-intent-dataset/base_locale/fi/area.entity @@ -0,0 +1,12 @@ +keittiö +olohuone +makuuhuone +kylpyhuone +toimisto +autotalli +käytävä +kellari +ruokailuhuone +puutarha +terassi +pesula diff --git a/examples/hass-intent-dataset/base_locale/fi/color.entity b/examples/hass-intent-dataset/base_locale/fi/color.entity new file mode 100644 index 00000000..742fbf65 --- /dev/null +++ b/examples/hass-intent-dataset/base_locale/fi/color.entity @@ -0,0 +1,11 @@ +valkoinen +musta +punainen +oranssi +keltainen +vihreä +sininen +violetti +ruskea +vaaleanpunainen +turkoosi diff --git a/examples/hass-intent-dataset/base_locale/fi/device_class.entity b/examples/hass-intent-dataset/base_locale/fi/device_class.entity new file mode 100644 index 00000000..8878fb15 --- /dev/null +++ b/examples/hass-intent-dataset/base_locale/fi/device_class.entity @@ -0,0 +1,32 @@ +awning +blind +curtain +door +garage +gate +shade +shutter +window +battery +carbon_monoxide +cold +connectivity +gas +heat +light +lock +moisture +motion +occupancy +opening +plug +power +presence +problem +running +safety +smoke +sound +tamper +update +vibration diff --git a/examples/hass-intent-dataset/base_locale/fi/domain.entity b/examples/hass-intent-dataset/base_locale/fi/domain.entity new file mode 100644 index 00000000..e062292e --- /dev/null +++ b/examples/hass-intent-dataset/base_locale/fi/domain.entity @@ -0,0 +1,19 @@ +light +fan +switch +cover +climate +media_player +sensor +binary_sensor +lock +vacuum +timer +input_boolean +scene +script +automation +weather +camera +humidifier +water_heater diff --git a/examples/hass-intent-dataset/base_locale/fi/floor.entity b/examples/hass-intent-dataset/base_locale/fi/floor.entity new file mode 100644 index 00000000..41f8ddbc --- /dev/null +++ b/examples/hass-intent-dataset/base_locale/fi/floor.entity @@ -0,0 +1,5 @@ +ground floor +first floor +second floor +basement +attic diff --git a/examples/hass-intent-dataset/base_locale/fi/name.entity b/examples/hass-intent-dataset/base_locale/fi/name.entity new file mode 100644 index 00000000..b40f02a9 --- /dev/null +++ b/examples/hass-intent-dataset/base_locale/fi/name.entity @@ -0,0 +1,10 @@ +kitchen light +living room light +bedroom light +ceiling fan +thermostat +tv +speaker +front door +garage door +vacuum diff --git a/examples/hass-intent-dataset/base_locale/fi/state.entity b/examples/hass-intent-dataset/base_locale/fi/state.entity new file mode 100644 index 00000000..16d915c8 --- /dev/null +++ b/examples/hass-intent-dataset/base_locale/fi/state.entity @@ -0,0 +1,6 @@ +päällä +pois +auki +kiinni +lukittu +avattu diff --git a/examples/hass-intent-dataset/base_locale/fr/area.entity b/examples/hass-intent-dataset/base_locale/fr/area.entity new file mode 100644 index 00000000..87f9a7a5 --- /dev/null +++ b/examples/hass-intent-dataset/base_locale/fr/area.entity @@ -0,0 +1,12 @@ +cuisine +salon +chambre +salle de bain +bureau +garage +couloir +sous-sol +salle à manger +jardin +terrasse +buanderie diff --git a/examples/hass-intent-dataset/base_locale/fr/color.entity b/examples/hass-intent-dataset/base_locale/fr/color.entity new file mode 100644 index 00000000..29a55c3e --- /dev/null +++ b/examples/hass-intent-dataset/base_locale/fr/color.entity @@ -0,0 +1,11 @@ +blanc +noir +rouge +orange +jaune +vert +bleu +violet +marron +rose +turquoise diff --git a/examples/hass-intent-dataset/base_locale/fr/device_class.entity b/examples/hass-intent-dataset/base_locale/fr/device_class.entity new file mode 100644 index 00000000..8878fb15 --- /dev/null +++ b/examples/hass-intent-dataset/base_locale/fr/device_class.entity @@ -0,0 +1,32 @@ +awning +blind +curtain +door +garage +gate +shade +shutter +window +battery +carbon_monoxide +cold +connectivity +gas +heat +light +lock +moisture +motion +occupancy +opening +plug +power +presence +problem +running +safety +smoke +sound +tamper +update +vibration diff --git a/examples/hass-intent-dataset/base_locale/fr/domain.entity b/examples/hass-intent-dataset/base_locale/fr/domain.entity new file mode 100644 index 00000000..e062292e --- /dev/null +++ b/examples/hass-intent-dataset/base_locale/fr/domain.entity @@ -0,0 +1,19 @@ +light +fan +switch +cover +climate +media_player +sensor +binary_sensor +lock +vacuum +timer +input_boolean +scene +script +automation +weather +camera +humidifier +water_heater diff --git a/examples/hass-intent-dataset/base_locale/fr/floor.entity b/examples/hass-intent-dataset/base_locale/fr/floor.entity new file mode 100644 index 00000000..7c7626d1 --- /dev/null +++ b/examples/hass-intent-dataset/base_locale/fr/floor.entity @@ -0,0 +1,5 @@ +rez-de-chaussée +premier étage +deuxième étage +sous-sol +grenier diff --git a/examples/hass-intent-dataset/base_locale/fr/name.entity b/examples/hass-intent-dataset/base_locale/fr/name.entity new file mode 100644 index 00000000..d20837cf --- /dev/null +++ b/examples/hass-intent-dataset/base_locale/fr/name.entity @@ -0,0 +1,10 @@ +lumière de la cuisine +lumière du salon +lumière de la chambre +ventilateur de plafond +thermostat +télévision +haut-parleur +porte d'entrée +porte du garage +aspirateur diff --git a/examples/hass-intent-dataset/base_locale/fr/state.entity b/examples/hass-intent-dataset/base_locale/fr/state.entity new file mode 100644 index 00000000..3fa81d93 --- /dev/null +++ b/examples/hass-intent-dataset/base_locale/fr/state.entity @@ -0,0 +1,6 @@ +allumé +éteint +ouvert +fermé +verrouillé +déverrouillé diff --git a/examples/hass-intent-dataset/base_locale/ga/area.entity b/examples/hass-intent-dataset/base_locale/ga/area.entity new file mode 100644 index 00000000..5524f2f7 --- /dev/null +++ b/examples/hass-intent-dataset/base_locale/ga/area.entity @@ -0,0 +1,12 @@ +cistin +seomra suí +seomra leapa +seomra folctha +oifig +garáiste +halla +íseallán +seomra bia +gairdín +ardán +seomra níocháin diff --git a/examples/hass-intent-dataset/base_locale/ga/color.entity b/examples/hass-intent-dataset/base_locale/ga/color.entity new file mode 100644 index 00000000..76285fdf --- /dev/null +++ b/examples/hass-intent-dataset/base_locale/ga/color.entity @@ -0,0 +1,11 @@ +white +black +red +orange +yellow +green +blue +purple +brown +pink +turquoise diff --git a/examples/hass-intent-dataset/base_locale/ga/device_class.entity b/examples/hass-intent-dataset/base_locale/ga/device_class.entity new file mode 100644 index 00000000..8878fb15 --- /dev/null +++ b/examples/hass-intent-dataset/base_locale/ga/device_class.entity @@ -0,0 +1,32 @@ +awning +blind +curtain +door +garage +gate +shade +shutter +window +battery +carbon_monoxide +cold +connectivity +gas +heat +light +lock +moisture +motion +occupancy +opening +plug +power +presence +problem +running +safety +smoke +sound +tamper +update +vibration diff --git a/examples/hass-intent-dataset/base_locale/ga/domain.entity b/examples/hass-intent-dataset/base_locale/ga/domain.entity new file mode 100644 index 00000000..e062292e --- /dev/null +++ b/examples/hass-intent-dataset/base_locale/ga/domain.entity @@ -0,0 +1,19 @@ +light +fan +switch +cover +climate +media_player +sensor +binary_sensor +lock +vacuum +timer +input_boolean +scene +script +automation +weather +camera +humidifier +water_heater diff --git a/examples/hass-intent-dataset/base_locale/ga/floor.entity b/examples/hass-intent-dataset/base_locale/ga/floor.entity new file mode 100644 index 00000000..41f8ddbc --- /dev/null +++ b/examples/hass-intent-dataset/base_locale/ga/floor.entity @@ -0,0 +1,5 @@ +ground floor +first floor +second floor +basement +attic diff --git a/examples/hass-intent-dataset/base_locale/ga/name.entity b/examples/hass-intent-dataset/base_locale/ga/name.entity new file mode 100644 index 00000000..b40f02a9 --- /dev/null +++ b/examples/hass-intent-dataset/base_locale/ga/name.entity @@ -0,0 +1,10 @@ +kitchen light +living room light +bedroom light +ceiling fan +thermostat +tv +speaker +front door +garage door +vacuum diff --git a/examples/hass-intent-dataset/base_locale/ga/state.entity b/examples/hass-intent-dataset/base_locale/ga/state.entity new file mode 100644 index 00000000..4faa6b78 --- /dev/null +++ b/examples/hass-intent-dataset/base_locale/ga/state.entity @@ -0,0 +1,6 @@ +on +off +open +closed +locked +unlocked diff --git a/examples/hass-intent-dataset/base_locale/gl/area.entity b/examples/hass-intent-dataset/base_locale/gl/area.entity new file mode 100644 index 00000000..dddc3a71 --- /dev/null +++ b/examples/hass-intent-dataset/base_locale/gl/area.entity @@ -0,0 +1,12 @@ +cociña +sala de estar +cuarto +baño +oficina +garaxe +corredor +soto +comedor +xardín +terraza +lavandería diff --git a/examples/hass-intent-dataset/base_locale/gl/color.entity b/examples/hass-intent-dataset/base_locale/gl/color.entity new file mode 100644 index 00000000..76285fdf --- /dev/null +++ b/examples/hass-intent-dataset/base_locale/gl/color.entity @@ -0,0 +1,11 @@ +white +black +red +orange +yellow +green +blue +purple +brown +pink +turquoise diff --git a/examples/hass-intent-dataset/base_locale/gl/device_class.entity b/examples/hass-intent-dataset/base_locale/gl/device_class.entity new file mode 100644 index 00000000..8878fb15 --- /dev/null +++ b/examples/hass-intent-dataset/base_locale/gl/device_class.entity @@ -0,0 +1,32 @@ +awning +blind +curtain +door +garage +gate +shade +shutter +window +battery +carbon_monoxide +cold +connectivity +gas +heat +light +lock +moisture +motion +occupancy +opening +plug +power +presence +problem +running +safety +smoke +sound +tamper +update +vibration diff --git a/examples/hass-intent-dataset/base_locale/gl/domain.entity b/examples/hass-intent-dataset/base_locale/gl/domain.entity new file mode 100644 index 00000000..e062292e --- /dev/null +++ b/examples/hass-intent-dataset/base_locale/gl/domain.entity @@ -0,0 +1,19 @@ +light +fan +switch +cover +climate +media_player +sensor +binary_sensor +lock +vacuum +timer +input_boolean +scene +script +automation +weather +camera +humidifier +water_heater diff --git a/examples/hass-intent-dataset/base_locale/gl/floor.entity b/examples/hass-intent-dataset/base_locale/gl/floor.entity new file mode 100644 index 00000000..41f8ddbc --- /dev/null +++ b/examples/hass-intent-dataset/base_locale/gl/floor.entity @@ -0,0 +1,5 @@ +ground floor +first floor +second floor +basement +attic diff --git a/examples/hass-intent-dataset/base_locale/gl/name.entity b/examples/hass-intent-dataset/base_locale/gl/name.entity new file mode 100644 index 00000000..b40f02a9 --- /dev/null +++ b/examples/hass-intent-dataset/base_locale/gl/name.entity @@ -0,0 +1,10 @@ +kitchen light +living room light +bedroom light +ceiling fan +thermostat +tv +speaker +front door +garage door +vacuum diff --git a/examples/hass-intent-dataset/base_locale/gl/state.entity b/examples/hass-intent-dataset/base_locale/gl/state.entity new file mode 100644 index 00000000..4faa6b78 --- /dev/null +++ b/examples/hass-intent-dataset/base_locale/gl/state.entity @@ -0,0 +1,6 @@ +on +off +open +closed +locked +unlocked diff --git a/examples/hass-intent-dataset/base_locale/gu/area.entity b/examples/hass-intent-dataset/base_locale/gu/area.entity new file mode 100644 index 00000000..1c487f90 --- /dev/null +++ b/examples/hass-intent-dataset/base_locale/gu/area.entity @@ -0,0 +1,12 @@ +રસોડું +લિવિંગ રૂમ +બેડરૂમ +બાથરૂમ +ઓફિસ +ગેરેજ +કોરિડોર +ભોંયરું +ડાઇનિંગ રૂમ +બગીચો +ટેરેસ +લોન્ડ્રી રૂમ diff --git a/examples/hass-intent-dataset/base_locale/gu/color.entity b/examples/hass-intent-dataset/base_locale/gu/color.entity new file mode 100644 index 00000000..76285fdf --- /dev/null +++ b/examples/hass-intent-dataset/base_locale/gu/color.entity @@ -0,0 +1,11 @@ +white +black +red +orange +yellow +green +blue +purple +brown +pink +turquoise diff --git a/examples/hass-intent-dataset/base_locale/gu/device_class.entity b/examples/hass-intent-dataset/base_locale/gu/device_class.entity new file mode 100644 index 00000000..8878fb15 --- /dev/null +++ b/examples/hass-intent-dataset/base_locale/gu/device_class.entity @@ -0,0 +1,32 @@ +awning +blind +curtain +door +garage +gate +shade +shutter +window +battery +carbon_monoxide +cold +connectivity +gas +heat +light +lock +moisture +motion +occupancy +opening +plug +power +presence +problem +running +safety +smoke +sound +tamper +update +vibration diff --git a/examples/hass-intent-dataset/base_locale/gu/domain.entity b/examples/hass-intent-dataset/base_locale/gu/domain.entity new file mode 100644 index 00000000..e062292e --- /dev/null +++ b/examples/hass-intent-dataset/base_locale/gu/domain.entity @@ -0,0 +1,19 @@ +light +fan +switch +cover +climate +media_player +sensor +binary_sensor +lock +vacuum +timer +input_boolean +scene +script +automation +weather +camera +humidifier +water_heater diff --git a/examples/hass-intent-dataset/base_locale/gu/floor.entity b/examples/hass-intent-dataset/base_locale/gu/floor.entity new file mode 100644 index 00000000..41f8ddbc --- /dev/null +++ b/examples/hass-intent-dataset/base_locale/gu/floor.entity @@ -0,0 +1,5 @@ +ground floor +first floor +second floor +basement +attic diff --git a/examples/hass-intent-dataset/base_locale/gu/name.entity b/examples/hass-intent-dataset/base_locale/gu/name.entity new file mode 100644 index 00000000..b40f02a9 --- /dev/null +++ b/examples/hass-intent-dataset/base_locale/gu/name.entity @@ -0,0 +1,10 @@ +kitchen light +living room light +bedroom light +ceiling fan +thermostat +tv +speaker +front door +garage door +vacuum diff --git a/examples/hass-intent-dataset/base_locale/gu/state.entity b/examples/hass-intent-dataset/base_locale/gu/state.entity new file mode 100644 index 00000000..4faa6b78 --- /dev/null +++ b/examples/hass-intent-dataset/base_locale/gu/state.entity @@ -0,0 +1,6 @@ +on +off +open +closed +locked +unlocked diff --git a/examples/hass-intent-dataset/base_locale/he/area.entity b/examples/hass-intent-dataset/base_locale/he/area.entity new file mode 100644 index 00000000..b3ef6072 --- /dev/null +++ b/examples/hass-intent-dataset/base_locale/he/area.entity @@ -0,0 +1,12 @@ +מטבח +סלון +חדר שינה +אמבטיה +משרד +מוסך +מסדרון +מרתף +חדר אוכל +גן +מרפסת +חדר כביסה diff --git a/examples/hass-intent-dataset/base_locale/he/color.entity b/examples/hass-intent-dataset/base_locale/he/color.entity new file mode 100644 index 00000000..76285fdf --- /dev/null +++ b/examples/hass-intent-dataset/base_locale/he/color.entity @@ -0,0 +1,11 @@ +white +black +red +orange +yellow +green +blue +purple +brown +pink +turquoise diff --git a/examples/hass-intent-dataset/base_locale/he/device_class.entity b/examples/hass-intent-dataset/base_locale/he/device_class.entity new file mode 100644 index 00000000..8878fb15 --- /dev/null +++ b/examples/hass-intent-dataset/base_locale/he/device_class.entity @@ -0,0 +1,32 @@ +awning +blind +curtain +door +garage +gate +shade +shutter +window +battery +carbon_monoxide +cold +connectivity +gas +heat +light +lock +moisture +motion +occupancy +opening +plug +power +presence +problem +running +safety +smoke +sound +tamper +update +vibration diff --git a/examples/hass-intent-dataset/base_locale/he/domain.entity b/examples/hass-intent-dataset/base_locale/he/domain.entity new file mode 100644 index 00000000..e062292e --- /dev/null +++ b/examples/hass-intent-dataset/base_locale/he/domain.entity @@ -0,0 +1,19 @@ +light +fan +switch +cover +climate +media_player +sensor +binary_sensor +lock +vacuum +timer +input_boolean +scene +script +automation +weather +camera +humidifier +water_heater diff --git a/examples/hass-intent-dataset/base_locale/he/floor.entity b/examples/hass-intent-dataset/base_locale/he/floor.entity new file mode 100644 index 00000000..41f8ddbc --- /dev/null +++ b/examples/hass-intent-dataset/base_locale/he/floor.entity @@ -0,0 +1,5 @@ +ground floor +first floor +second floor +basement +attic diff --git a/examples/hass-intent-dataset/base_locale/he/name.entity b/examples/hass-intent-dataset/base_locale/he/name.entity new file mode 100644 index 00000000..b40f02a9 --- /dev/null +++ b/examples/hass-intent-dataset/base_locale/he/name.entity @@ -0,0 +1,10 @@ +kitchen light +living room light +bedroom light +ceiling fan +thermostat +tv +speaker +front door +garage door +vacuum diff --git a/examples/hass-intent-dataset/base_locale/he/state.entity b/examples/hass-intent-dataset/base_locale/he/state.entity new file mode 100644 index 00000000..4faa6b78 --- /dev/null +++ b/examples/hass-intent-dataset/base_locale/he/state.entity @@ -0,0 +1,6 @@ +on +off +open +closed +locked +unlocked diff --git a/examples/hass-intent-dataset/base_locale/hi/area.entity b/examples/hass-intent-dataset/base_locale/hi/area.entity new file mode 100644 index 00000000..641fbd5c --- /dev/null +++ b/examples/hass-intent-dataset/base_locale/hi/area.entity @@ -0,0 +1,12 @@ +रसोई +लिविंग रूम +बेडरूम +बाथरूम +कार्यालय +गैरेज +गलियारा +तहखाना +भोजन कक्ष +बगीचा +छत +कपड़े धोने का कमरा diff --git a/examples/hass-intent-dataset/base_locale/hi/color.entity b/examples/hass-intent-dataset/base_locale/hi/color.entity new file mode 100644 index 00000000..76285fdf --- /dev/null +++ b/examples/hass-intent-dataset/base_locale/hi/color.entity @@ -0,0 +1,11 @@ +white +black +red +orange +yellow +green +blue +purple +brown +pink +turquoise diff --git a/examples/hass-intent-dataset/base_locale/hi/device_class.entity b/examples/hass-intent-dataset/base_locale/hi/device_class.entity new file mode 100644 index 00000000..8878fb15 --- /dev/null +++ b/examples/hass-intent-dataset/base_locale/hi/device_class.entity @@ -0,0 +1,32 @@ +awning +blind +curtain +door +garage +gate +shade +shutter +window +battery +carbon_monoxide +cold +connectivity +gas +heat +light +lock +moisture +motion +occupancy +opening +plug +power +presence +problem +running +safety +smoke +sound +tamper +update +vibration diff --git a/examples/hass-intent-dataset/base_locale/hi/domain.entity b/examples/hass-intent-dataset/base_locale/hi/domain.entity new file mode 100644 index 00000000..e062292e --- /dev/null +++ b/examples/hass-intent-dataset/base_locale/hi/domain.entity @@ -0,0 +1,19 @@ +light +fan +switch +cover +climate +media_player +sensor +binary_sensor +lock +vacuum +timer +input_boolean +scene +script +automation +weather +camera +humidifier +water_heater diff --git a/examples/hass-intent-dataset/base_locale/hi/floor.entity b/examples/hass-intent-dataset/base_locale/hi/floor.entity new file mode 100644 index 00000000..41f8ddbc --- /dev/null +++ b/examples/hass-intent-dataset/base_locale/hi/floor.entity @@ -0,0 +1,5 @@ +ground floor +first floor +second floor +basement +attic diff --git a/examples/hass-intent-dataset/base_locale/hi/name.entity b/examples/hass-intent-dataset/base_locale/hi/name.entity new file mode 100644 index 00000000..b40f02a9 --- /dev/null +++ b/examples/hass-intent-dataset/base_locale/hi/name.entity @@ -0,0 +1,10 @@ +kitchen light +living room light +bedroom light +ceiling fan +thermostat +tv +speaker +front door +garage door +vacuum diff --git a/examples/hass-intent-dataset/base_locale/hi/state.entity b/examples/hass-intent-dataset/base_locale/hi/state.entity new file mode 100644 index 00000000..4faa6b78 --- /dev/null +++ b/examples/hass-intent-dataset/base_locale/hi/state.entity @@ -0,0 +1,6 @@ +on +off +open +closed +locked +unlocked diff --git a/examples/hass-intent-dataset/base_locale/hr/area.entity b/examples/hass-intent-dataset/base_locale/hr/area.entity new file mode 100644 index 00000000..41b84689 --- /dev/null +++ b/examples/hass-intent-dataset/base_locale/hr/area.entity @@ -0,0 +1,12 @@ +kuhinja +dnevni boravak +spavaća soba +kupaonica +ured +garaža +hodnik +podrum +blagovaonica +vrt +terasa +praonica diff --git a/examples/hass-intent-dataset/base_locale/hr/color.entity b/examples/hass-intent-dataset/base_locale/hr/color.entity new file mode 100644 index 00000000..76285fdf --- /dev/null +++ b/examples/hass-intent-dataset/base_locale/hr/color.entity @@ -0,0 +1,11 @@ +white +black +red +orange +yellow +green +blue +purple +brown +pink +turquoise diff --git a/examples/hass-intent-dataset/base_locale/hr/device_class.entity b/examples/hass-intent-dataset/base_locale/hr/device_class.entity new file mode 100644 index 00000000..8878fb15 --- /dev/null +++ b/examples/hass-intent-dataset/base_locale/hr/device_class.entity @@ -0,0 +1,32 @@ +awning +blind +curtain +door +garage +gate +shade +shutter +window +battery +carbon_monoxide +cold +connectivity +gas +heat +light +lock +moisture +motion +occupancy +opening +plug +power +presence +problem +running +safety +smoke +sound +tamper +update +vibration diff --git a/examples/hass-intent-dataset/base_locale/hr/domain.entity b/examples/hass-intent-dataset/base_locale/hr/domain.entity new file mode 100644 index 00000000..e062292e --- /dev/null +++ b/examples/hass-intent-dataset/base_locale/hr/domain.entity @@ -0,0 +1,19 @@ +light +fan +switch +cover +climate +media_player +sensor +binary_sensor +lock +vacuum +timer +input_boolean +scene +script +automation +weather +camera +humidifier +water_heater diff --git a/examples/hass-intent-dataset/base_locale/hr/floor.entity b/examples/hass-intent-dataset/base_locale/hr/floor.entity new file mode 100644 index 00000000..41f8ddbc --- /dev/null +++ b/examples/hass-intent-dataset/base_locale/hr/floor.entity @@ -0,0 +1,5 @@ +ground floor +first floor +second floor +basement +attic diff --git a/examples/hass-intent-dataset/base_locale/hr/name.entity b/examples/hass-intent-dataset/base_locale/hr/name.entity new file mode 100644 index 00000000..b40f02a9 --- /dev/null +++ b/examples/hass-intent-dataset/base_locale/hr/name.entity @@ -0,0 +1,10 @@ +kitchen light +living room light +bedroom light +ceiling fan +thermostat +tv +speaker +front door +garage door +vacuum diff --git a/examples/hass-intent-dataset/base_locale/hr/state.entity b/examples/hass-intent-dataset/base_locale/hr/state.entity new file mode 100644 index 00000000..4faa6b78 --- /dev/null +++ b/examples/hass-intent-dataset/base_locale/hr/state.entity @@ -0,0 +1,6 @@ +on +off +open +closed +locked +unlocked diff --git a/examples/hass-intent-dataset/base_locale/hu/area.entity b/examples/hass-intent-dataset/base_locale/hu/area.entity new file mode 100644 index 00000000..6bc05f07 --- /dev/null +++ b/examples/hass-intent-dataset/base_locale/hu/area.entity @@ -0,0 +1,12 @@ +konyha +nappali +hálószoba +fürdőszoba +iroda +garázs +folyosó +pince +étkező +kert +terasz +mosókonyha diff --git a/examples/hass-intent-dataset/base_locale/hu/color.entity b/examples/hass-intent-dataset/base_locale/hu/color.entity new file mode 100644 index 00000000..76285fdf --- /dev/null +++ b/examples/hass-intent-dataset/base_locale/hu/color.entity @@ -0,0 +1,11 @@ +white +black +red +orange +yellow +green +blue +purple +brown +pink +turquoise diff --git a/examples/hass-intent-dataset/base_locale/hu/device_class.entity b/examples/hass-intent-dataset/base_locale/hu/device_class.entity new file mode 100644 index 00000000..8878fb15 --- /dev/null +++ b/examples/hass-intent-dataset/base_locale/hu/device_class.entity @@ -0,0 +1,32 @@ +awning +blind +curtain +door +garage +gate +shade +shutter +window +battery +carbon_monoxide +cold +connectivity +gas +heat +light +lock +moisture +motion +occupancy +opening +plug +power +presence +problem +running +safety +smoke +sound +tamper +update +vibration diff --git a/examples/hass-intent-dataset/base_locale/hu/domain.entity b/examples/hass-intent-dataset/base_locale/hu/domain.entity new file mode 100644 index 00000000..e062292e --- /dev/null +++ b/examples/hass-intent-dataset/base_locale/hu/domain.entity @@ -0,0 +1,19 @@ +light +fan +switch +cover +climate +media_player +sensor +binary_sensor +lock +vacuum +timer +input_boolean +scene +script +automation +weather +camera +humidifier +water_heater diff --git a/examples/hass-intent-dataset/base_locale/hu/floor.entity b/examples/hass-intent-dataset/base_locale/hu/floor.entity new file mode 100644 index 00000000..41f8ddbc --- /dev/null +++ b/examples/hass-intent-dataset/base_locale/hu/floor.entity @@ -0,0 +1,5 @@ +ground floor +first floor +second floor +basement +attic diff --git a/examples/hass-intent-dataset/base_locale/hu/name.entity b/examples/hass-intent-dataset/base_locale/hu/name.entity new file mode 100644 index 00000000..b40f02a9 --- /dev/null +++ b/examples/hass-intent-dataset/base_locale/hu/name.entity @@ -0,0 +1,10 @@ +kitchen light +living room light +bedroom light +ceiling fan +thermostat +tv +speaker +front door +garage door +vacuum diff --git a/examples/hass-intent-dataset/base_locale/hu/state.entity b/examples/hass-intent-dataset/base_locale/hu/state.entity new file mode 100644 index 00000000..4faa6b78 --- /dev/null +++ b/examples/hass-intent-dataset/base_locale/hu/state.entity @@ -0,0 +1,6 @@ +on +off +open +closed +locked +unlocked diff --git a/examples/hass-intent-dataset/base_locale/is/area.entity b/examples/hass-intent-dataset/base_locale/is/area.entity new file mode 100644 index 00000000..9c3f671d --- /dev/null +++ b/examples/hass-intent-dataset/base_locale/is/area.entity @@ -0,0 +1,12 @@ +eldhús +stofa +svefnherbergi +baðherbergi +skrifstofa +bílskúr +gangur +kjallari +borðstofa +garður +verönd +þvottahús diff --git a/examples/hass-intent-dataset/base_locale/is/color.entity b/examples/hass-intent-dataset/base_locale/is/color.entity new file mode 100644 index 00000000..76285fdf --- /dev/null +++ b/examples/hass-intent-dataset/base_locale/is/color.entity @@ -0,0 +1,11 @@ +white +black +red +orange +yellow +green +blue +purple +brown +pink +turquoise diff --git a/examples/hass-intent-dataset/base_locale/is/device_class.entity b/examples/hass-intent-dataset/base_locale/is/device_class.entity new file mode 100644 index 00000000..8878fb15 --- /dev/null +++ b/examples/hass-intent-dataset/base_locale/is/device_class.entity @@ -0,0 +1,32 @@ +awning +blind +curtain +door +garage +gate +shade +shutter +window +battery +carbon_monoxide +cold +connectivity +gas +heat +light +lock +moisture +motion +occupancy +opening +plug +power +presence +problem +running +safety +smoke +sound +tamper +update +vibration diff --git a/examples/hass-intent-dataset/base_locale/is/domain.entity b/examples/hass-intent-dataset/base_locale/is/domain.entity new file mode 100644 index 00000000..e062292e --- /dev/null +++ b/examples/hass-intent-dataset/base_locale/is/domain.entity @@ -0,0 +1,19 @@ +light +fan +switch +cover +climate +media_player +sensor +binary_sensor +lock +vacuum +timer +input_boolean +scene +script +automation +weather +camera +humidifier +water_heater diff --git a/examples/hass-intent-dataset/base_locale/is/floor.entity b/examples/hass-intent-dataset/base_locale/is/floor.entity new file mode 100644 index 00000000..41f8ddbc --- /dev/null +++ b/examples/hass-intent-dataset/base_locale/is/floor.entity @@ -0,0 +1,5 @@ +ground floor +first floor +second floor +basement +attic diff --git a/examples/hass-intent-dataset/base_locale/is/name.entity b/examples/hass-intent-dataset/base_locale/is/name.entity new file mode 100644 index 00000000..b40f02a9 --- /dev/null +++ b/examples/hass-intent-dataset/base_locale/is/name.entity @@ -0,0 +1,10 @@ +kitchen light +living room light +bedroom light +ceiling fan +thermostat +tv +speaker +front door +garage door +vacuum diff --git a/examples/hass-intent-dataset/base_locale/is/state.entity b/examples/hass-intent-dataset/base_locale/is/state.entity new file mode 100644 index 00000000..4faa6b78 --- /dev/null +++ b/examples/hass-intent-dataset/base_locale/is/state.entity @@ -0,0 +1,6 @@ +on +off +open +closed +locked +unlocked diff --git a/examples/hass-intent-dataset/base_locale/it/area.entity b/examples/hass-intent-dataset/base_locale/it/area.entity new file mode 100644 index 00000000..fdd35173 --- /dev/null +++ b/examples/hass-intent-dataset/base_locale/it/area.entity @@ -0,0 +1,12 @@ +cucina +soggiorno +camera da letto +bagno +ufficio +garage +corridoio +seminterrato +sala da pranzo +giardino +terrazza +lavanderia diff --git a/examples/hass-intent-dataset/base_locale/it/color.entity b/examples/hass-intent-dataset/base_locale/it/color.entity new file mode 100644 index 00000000..68bf6d69 --- /dev/null +++ b/examples/hass-intent-dataset/base_locale/it/color.entity @@ -0,0 +1,11 @@ +bianco +nero +rosso +arancione +giallo +verde +blu +viola +marrone +rosa +turchese diff --git a/examples/hass-intent-dataset/base_locale/it/device_class.entity b/examples/hass-intent-dataset/base_locale/it/device_class.entity new file mode 100644 index 00000000..8878fb15 --- /dev/null +++ b/examples/hass-intent-dataset/base_locale/it/device_class.entity @@ -0,0 +1,32 @@ +awning +blind +curtain +door +garage +gate +shade +shutter +window +battery +carbon_monoxide +cold +connectivity +gas +heat +light +lock +moisture +motion +occupancy +opening +plug +power +presence +problem +running +safety +smoke +sound +tamper +update +vibration diff --git a/examples/hass-intent-dataset/base_locale/it/domain.entity b/examples/hass-intent-dataset/base_locale/it/domain.entity new file mode 100644 index 00000000..e062292e --- /dev/null +++ b/examples/hass-intent-dataset/base_locale/it/domain.entity @@ -0,0 +1,19 @@ +light +fan +switch +cover +climate +media_player +sensor +binary_sensor +lock +vacuum +timer +input_boolean +scene +script +automation +weather +camera +humidifier +water_heater diff --git a/examples/hass-intent-dataset/base_locale/it/floor.entity b/examples/hass-intent-dataset/base_locale/it/floor.entity new file mode 100644 index 00000000..0fcb6541 --- /dev/null +++ b/examples/hass-intent-dataset/base_locale/it/floor.entity @@ -0,0 +1,5 @@ +piano terra +primo piano +secondo piano +seminterrato +soffitta diff --git a/examples/hass-intent-dataset/base_locale/it/name.entity b/examples/hass-intent-dataset/base_locale/it/name.entity new file mode 100644 index 00000000..65c3207a --- /dev/null +++ b/examples/hass-intent-dataset/base_locale/it/name.entity @@ -0,0 +1,10 @@ +luce cucina +luce soggiorno +luce camera da letto +ventilatore a soffitto +termostato +televisione +altoparlante +porta principale +porta del garage +aspirapolvere diff --git a/examples/hass-intent-dataset/base_locale/it/state.entity b/examples/hass-intent-dataset/base_locale/it/state.entity new file mode 100644 index 00000000..ee5a4639 --- /dev/null +++ b/examples/hass-intent-dataset/base_locale/it/state.entity @@ -0,0 +1,6 @@ +acceso +spento +aperto +chiuso +bloccato +sbloccato diff --git a/examples/hass-intent-dataset/base_locale/ja/area.entity b/examples/hass-intent-dataset/base_locale/ja/area.entity new file mode 100644 index 00000000..e0b0d06b --- /dev/null +++ b/examples/hass-intent-dataset/base_locale/ja/area.entity @@ -0,0 +1,12 @@ +キッチン +リビング +寝室 +浴室 +書斎 +ガレージ +廊下 +地下室 +ダイニング +庭 +テラス +洗濯室 diff --git a/examples/hass-intent-dataset/base_locale/ja/color.entity b/examples/hass-intent-dataset/base_locale/ja/color.entity new file mode 100644 index 00000000..222b5665 --- /dev/null +++ b/examples/hass-intent-dataset/base_locale/ja/color.entity @@ -0,0 +1,11 @@ +白 +黒 +赤 +橙 +黄 +緑 +青 +紫 +茶 +桃 +水色 diff --git a/examples/hass-intent-dataset/base_locale/ja/device_class.entity b/examples/hass-intent-dataset/base_locale/ja/device_class.entity new file mode 100644 index 00000000..8878fb15 --- /dev/null +++ b/examples/hass-intent-dataset/base_locale/ja/device_class.entity @@ -0,0 +1,32 @@ +awning +blind +curtain +door +garage +gate +shade +shutter +window +battery +carbon_monoxide +cold +connectivity +gas +heat +light +lock +moisture +motion +occupancy +opening +plug +power +presence +problem +running +safety +smoke +sound +tamper +update +vibration diff --git a/examples/hass-intent-dataset/base_locale/ja/domain.entity b/examples/hass-intent-dataset/base_locale/ja/domain.entity new file mode 100644 index 00000000..e062292e --- /dev/null +++ b/examples/hass-intent-dataset/base_locale/ja/domain.entity @@ -0,0 +1,19 @@ +light +fan +switch +cover +climate +media_player +sensor +binary_sensor +lock +vacuum +timer +input_boolean +scene +script +automation +weather +camera +humidifier +water_heater diff --git a/examples/hass-intent-dataset/base_locale/ja/floor.entity b/examples/hass-intent-dataset/base_locale/ja/floor.entity new file mode 100644 index 00000000..3029294e --- /dev/null +++ b/examples/hass-intent-dataset/base_locale/ja/floor.entity @@ -0,0 +1,5 @@ +1階 +2階 +3階 +地下室 +屋根裏 diff --git a/examples/hass-intent-dataset/base_locale/ja/name.entity b/examples/hass-intent-dataset/base_locale/ja/name.entity new file mode 100644 index 00000000..f3a1a158 --- /dev/null +++ b/examples/hass-intent-dataset/base_locale/ja/name.entity @@ -0,0 +1,10 @@ +キッチンの照明 +リビングの照明 +寝室の照明 +シーリングファン +サーモスタット +テレビ +スピーカー +玄関 +ガレージのドア +掃除機 diff --git a/examples/hass-intent-dataset/base_locale/ja/state.entity b/examples/hass-intent-dataset/base_locale/ja/state.entity new file mode 100644 index 00000000..07345954 --- /dev/null +++ b/examples/hass-intent-dataset/base_locale/ja/state.entity @@ -0,0 +1,6 @@ +オン +オフ +開 +閉 +施錠 +解錠 diff --git a/examples/hass-intent-dataset/base_locale/ka/area.entity b/examples/hass-intent-dataset/base_locale/ka/area.entity new file mode 100644 index 00000000..f0b5dd20 --- /dev/null +++ b/examples/hass-intent-dataset/base_locale/ka/area.entity @@ -0,0 +1,12 @@ +სამზარეულო +მისაღები +საძინებელი +აბაზანა +ოფისი +ავტოფარეხი +დერეფანი +სარდაფი +სასადილო +ბაღი +ტერასა +სამრეცხაო diff --git a/examples/hass-intent-dataset/base_locale/ka/color.entity b/examples/hass-intent-dataset/base_locale/ka/color.entity new file mode 100644 index 00000000..76285fdf --- /dev/null +++ b/examples/hass-intent-dataset/base_locale/ka/color.entity @@ -0,0 +1,11 @@ +white +black +red +orange +yellow +green +blue +purple +brown +pink +turquoise diff --git a/examples/hass-intent-dataset/base_locale/ka/device_class.entity b/examples/hass-intent-dataset/base_locale/ka/device_class.entity new file mode 100644 index 00000000..8878fb15 --- /dev/null +++ b/examples/hass-intent-dataset/base_locale/ka/device_class.entity @@ -0,0 +1,32 @@ +awning +blind +curtain +door +garage +gate +shade +shutter +window +battery +carbon_monoxide +cold +connectivity +gas +heat +light +lock +moisture +motion +occupancy +opening +plug +power +presence +problem +running +safety +smoke +sound +tamper +update +vibration diff --git a/examples/hass-intent-dataset/base_locale/ka/domain.entity b/examples/hass-intent-dataset/base_locale/ka/domain.entity new file mode 100644 index 00000000..e062292e --- /dev/null +++ b/examples/hass-intent-dataset/base_locale/ka/domain.entity @@ -0,0 +1,19 @@ +light +fan +switch +cover +climate +media_player +sensor +binary_sensor +lock +vacuum +timer +input_boolean +scene +script +automation +weather +camera +humidifier +water_heater diff --git a/examples/hass-intent-dataset/base_locale/ka/floor.entity b/examples/hass-intent-dataset/base_locale/ka/floor.entity new file mode 100644 index 00000000..41f8ddbc --- /dev/null +++ b/examples/hass-intent-dataset/base_locale/ka/floor.entity @@ -0,0 +1,5 @@ +ground floor +first floor +second floor +basement +attic diff --git a/examples/hass-intent-dataset/base_locale/ka/name.entity b/examples/hass-intent-dataset/base_locale/ka/name.entity new file mode 100644 index 00000000..b40f02a9 --- /dev/null +++ b/examples/hass-intent-dataset/base_locale/ka/name.entity @@ -0,0 +1,10 @@ +kitchen light +living room light +bedroom light +ceiling fan +thermostat +tv +speaker +front door +garage door +vacuum diff --git a/examples/hass-intent-dataset/base_locale/ka/state.entity b/examples/hass-intent-dataset/base_locale/ka/state.entity new file mode 100644 index 00000000..4faa6b78 --- /dev/null +++ b/examples/hass-intent-dataset/base_locale/ka/state.entity @@ -0,0 +1,6 @@ +on +off +open +closed +locked +unlocked diff --git a/examples/hass-intent-dataset/base_locale/kn/area.entity b/examples/hass-intent-dataset/base_locale/kn/area.entity new file mode 100644 index 00000000..90e57f26 --- /dev/null +++ b/examples/hass-intent-dataset/base_locale/kn/area.entity @@ -0,0 +1,12 @@ +ಅಡುಗೆ ಮನೆ +ದುಡಿ ಕೊಠಡಿ +ಮಲಗು ಕೊಠಡಿ +ಸ್ನಾನಗೃಹ +ಕಚೇರಿ +ಗ್ಯಾರೇಜ್ +ಕಾರಿಡಾರ್ +ನೆಲಮಾಳಿಗೆ +ಊಟದ ಕೊಠಡಿ +ಉದ್ಯಾನ +ಟೆರೇಸ್ +ತೊಳೆಯುವ ಕೊಠಡಿ diff --git a/examples/hass-intent-dataset/base_locale/kn/color.entity b/examples/hass-intent-dataset/base_locale/kn/color.entity new file mode 100644 index 00000000..76285fdf --- /dev/null +++ b/examples/hass-intent-dataset/base_locale/kn/color.entity @@ -0,0 +1,11 @@ +white +black +red +orange +yellow +green +blue +purple +brown +pink +turquoise diff --git a/examples/hass-intent-dataset/base_locale/kn/device_class.entity b/examples/hass-intent-dataset/base_locale/kn/device_class.entity new file mode 100644 index 00000000..8878fb15 --- /dev/null +++ b/examples/hass-intent-dataset/base_locale/kn/device_class.entity @@ -0,0 +1,32 @@ +awning +blind +curtain +door +garage +gate +shade +shutter +window +battery +carbon_monoxide +cold +connectivity +gas +heat +light +lock +moisture +motion +occupancy +opening +plug +power +presence +problem +running +safety +smoke +sound +tamper +update +vibration diff --git a/examples/hass-intent-dataset/base_locale/kn/domain.entity b/examples/hass-intent-dataset/base_locale/kn/domain.entity new file mode 100644 index 00000000..e062292e --- /dev/null +++ b/examples/hass-intent-dataset/base_locale/kn/domain.entity @@ -0,0 +1,19 @@ +light +fan +switch +cover +climate +media_player +sensor +binary_sensor +lock +vacuum +timer +input_boolean +scene +script +automation +weather +camera +humidifier +water_heater diff --git a/examples/hass-intent-dataset/base_locale/kn/floor.entity b/examples/hass-intent-dataset/base_locale/kn/floor.entity new file mode 100644 index 00000000..41f8ddbc --- /dev/null +++ b/examples/hass-intent-dataset/base_locale/kn/floor.entity @@ -0,0 +1,5 @@ +ground floor +first floor +second floor +basement +attic diff --git a/examples/hass-intent-dataset/base_locale/kn/name.entity b/examples/hass-intent-dataset/base_locale/kn/name.entity new file mode 100644 index 00000000..b40f02a9 --- /dev/null +++ b/examples/hass-intent-dataset/base_locale/kn/name.entity @@ -0,0 +1,10 @@ +kitchen light +living room light +bedroom light +ceiling fan +thermostat +tv +speaker +front door +garage door +vacuum diff --git a/examples/hass-intent-dataset/base_locale/kn/state.entity b/examples/hass-intent-dataset/base_locale/kn/state.entity new file mode 100644 index 00000000..4faa6b78 --- /dev/null +++ b/examples/hass-intent-dataset/base_locale/kn/state.entity @@ -0,0 +1,6 @@ +on +off +open +closed +locked +unlocked diff --git a/examples/hass-intent-dataset/base_locale/ko/area.entity b/examples/hass-intent-dataset/base_locale/ko/area.entity new file mode 100644 index 00000000..9de66ddb --- /dev/null +++ b/examples/hass-intent-dataset/base_locale/ko/area.entity @@ -0,0 +1,12 @@ +부엌 +거실 +침실 +욕실 +사무실 +차고 +복도 +지하실 +식당 +정원 +테라스 +세탁실 diff --git a/examples/hass-intent-dataset/base_locale/ko/color.entity b/examples/hass-intent-dataset/base_locale/ko/color.entity new file mode 100644 index 00000000..a8fc049a --- /dev/null +++ b/examples/hass-intent-dataset/base_locale/ko/color.entity @@ -0,0 +1,11 @@ +흰색 +검은색 +빨간색 +주황색 +노란색 +초록색 +파란색 +보라색 +갈색 +분홍색 +청록색 diff --git a/examples/hass-intent-dataset/base_locale/ko/device_class.entity b/examples/hass-intent-dataset/base_locale/ko/device_class.entity new file mode 100644 index 00000000..8878fb15 --- /dev/null +++ b/examples/hass-intent-dataset/base_locale/ko/device_class.entity @@ -0,0 +1,32 @@ +awning +blind +curtain +door +garage +gate +shade +shutter +window +battery +carbon_monoxide +cold +connectivity +gas +heat +light +lock +moisture +motion +occupancy +opening +plug +power +presence +problem +running +safety +smoke +sound +tamper +update +vibration diff --git a/examples/hass-intent-dataset/base_locale/ko/domain.entity b/examples/hass-intent-dataset/base_locale/ko/domain.entity new file mode 100644 index 00000000..e062292e --- /dev/null +++ b/examples/hass-intent-dataset/base_locale/ko/domain.entity @@ -0,0 +1,19 @@ +light +fan +switch +cover +climate +media_player +sensor +binary_sensor +lock +vacuum +timer +input_boolean +scene +script +automation +weather +camera +humidifier +water_heater diff --git a/examples/hass-intent-dataset/base_locale/ko/floor.entity b/examples/hass-intent-dataset/base_locale/ko/floor.entity new file mode 100644 index 00000000..41f8ddbc --- /dev/null +++ b/examples/hass-intent-dataset/base_locale/ko/floor.entity @@ -0,0 +1,5 @@ +ground floor +first floor +second floor +basement +attic diff --git a/examples/hass-intent-dataset/base_locale/ko/name.entity b/examples/hass-intent-dataset/base_locale/ko/name.entity new file mode 100644 index 00000000..b40f02a9 --- /dev/null +++ b/examples/hass-intent-dataset/base_locale/ko/name.entity @@ -0,0 +1,10 @@ +kitchen light +living room light +bedroom light +ceiling fan +thermostat +tv +speaker +front door +garage door +vacuum diff --git a/examples/hass-intent-dataset/base_locale/ko/state.entity b/examples/hass-intent-dataset/base_locale/ko/state.entity new file mode 100644 index 00000000..8802dea6 --- /dev/null +++ b/examples/hass-intent-dataset/base_locale/ko/state.entity @@ -0,0 +1,6 @@ +켜짐 +꺼짐 +열림 +닫힘 +잠김 +잠금해제 diff --git a/examples/hass-intent-dataset/base_locale/kw/area.entity b/examples/hass-intent-dataset/base_locale/kw/area.entity new file mode 100644 index 00000000..6a577887 --- /dev/null +++ b/examples/hass-intent-dataset/base_locale/kw/area.entity @@ -0,0 +1,12 @@ +kek +rom godhesi +kewor +rom ymolchi +offis +garaj +koryor +kelder +rom dybri +lowarth +teras +rom yowghi diff --git a/examples/hass-intent-dataset/base_locale/kw/color.entity b/examples/hass-intent-dataset/base_locale/kw/color.entity new file mode 100644 index 00000000..76285fdf --- /dev/null +++ b/examples/hass-intent-dataset/base_locale/kw/color.entity @@ -0,0 +1,11 @@ +white +black +red +orange +yellow +green +blue +purple +brown +pink +turquoise diff --git a/examples/hass-intent-dataset/base_locale/kw/device_class.entity b/examples/hass-intent-dataset/base_locale/kw/device_class.entity new file mode 100644 index 00000000..8878fb15 --- /dev/null +++ b/examples/hass-intent-dataset/base_locale/kw/device_class.entity @@ -0,0 +1,32 @@ +awning +blind +curtain +door +garage +gate +shade +shutter +window +battery +carbon_monoxide +cold +connectivity +gas +heat +light +lock +moisture +motion +occupancy +opening +plug +power +presence +problem +running +safety +smoke +sound +tamper +update +vibration diff --git a/examples/hass-intent-dataset/base_locale/kw/domain.entity b/examples/hass-intent-dataset/base_locale/kw/domain.entity new file mode 100644 index 00000000..e062292e --- /dev/null +++ b/examples/hass-intent-dataset/base_locale/kw/domain.entity @@ -0,0 +1,19 @@ +light +fan +switch +cover +climate +media_player +sensor +binary_sensor +lock +vacuum +timer +input_boolean +scene +script +automation +weather +camera +humidifier +water_heater diff --git a/examples/hass-intent-dataset/base_locale/kw/floor.entity b/examples/hass-intent-dataset/base_locale/kw/floor.entity new file mode 100644 index 00000000..41f8ddbc --- /dev/null +++ b/examples/hass-intent-dataset/base_locale/kw/floor.entity @@ -0,0 +1,5 @@ +ground floor +first floor +second floor +basement +attic diff --git a/examples/hass-intent-dataset/base_locale/kw/name.entity b/examples/hass-intent-dataset/base_locale/kw/name.entity new file mode 100644 index 00000000..b40f02a9 --- /dev/null +++ b/examples/hass-intent-dataset/base_locale/kw/name.entity @@ -0,0 +1,10 @@ +kitchen light +living room light +bedroom light +ceiling fan +thermostat +tv +speaker +front door +garage door +vacuum diff --git a/examples/hass-intent-dataset/base_locale/kw/state.entity b/examples/hass-intent-dataset/base_locale/kw/state.entity new file mode 100644 index 00000000..4faa6b78 --- /dev/null +++ b/examples/hass-intent-dataset/base_locale/kw/state.entity @@ -0,0 +1,6 @@ +on +off +open +closed +locked +unlocked diff --git a/examples/hass-intent-dataset/base_locale/lb/area.entity b/examples/hass-intent-dataset/base_locale/lb/area.entity new file mode 100644 index 00000000..206fb332 --- /dev/null +++ b/examples/hass-intent-dataset/base_locale/lb/area.entity @@ -0,0 +1,12 @@ +Kichen +Wunnzëmmer +Schlofzëmmer +Buedzëmmer +Büro +Garage +Gank +Keller +Iesszëmmer +Gaart +Terrass +Wäschkichen diff --git a/examples/hass-intent-dataset/base_locale/lb/color.entity b/examples/hass-intent-dataset/base_locale/lb/color.entity new file mode 100644 index 00000000..76285fdf --- /dev/null +++ b/examples/hass-intent-dataset/base_locale/lb/color.entity @@ -0,0 +1,11 @@ +white +black +red +orange +yellow +green +blue +purple +brown +pink +turquoise diff --git a/examples/hass-intent-dataset/base_locale/lb/device_class.entity b/examples/hass-intent-dataset/base_locale/lb/device_class.entity new file mode 100644 index 00000000..8878fb15 --- /dev/null +++ b/examples/hass-intent-dataset/base_locale/lb/device_class.entity @@ -0,0 +1,32 @@ +awning +blind +curtain +door +garage +gate +shade +shutter +window +battery +carbon_monoxide +cold +connectivity +gas +heat +light +lock +moisture +motion +occupancy +opening +plug +power +presence +problem +running +safety +smoke +sound +tamper +update +vibration diff --git a/examples/hass-intent-dataset/base_locale/lb/domain.entity b/examples/hass-intent-dataset/base_locale/lb/domain.entity new file mode 100644 index 00000000..e062292e --- /dev/null +++ b/examples/hass-intent-dataset/base_locale/lb/domain.entity @@ -0,0 +1,19 @@ +light +fan +switch +cover +climate +media_player +sensor +binary_sensor +lock +vacuum +timer +input_boolean +scene +script +automation +weather +camera +humidifier +water_heater diff --git a/examples/hass-intent-dataset/base_locale/lb/floor.entity b/examples/hass-intent-dataset/base_locale/lb/floor.entity new file mode 100644 index 00000000..41f8ddbc --- /dev/null +++ b/examples/hass-intent-dataset/base_locale/lb/floor.entity @@ -0,0 +1,5 @@ +ground floor +first floor +second floor +basement +attic diff --git a/examples/hass-intent-dataset/base_locale/lb/name.entity b/examples/hass-intent-dataset/base_locale/lb/name.entity new file mode 100644 index 00000000..b40f02a9 --- /dev/null +++ b/examples/hass-intent-dataset/base_locale/lb/name.entity @@ -0,0 +1,10 @@ +kitchen light +living room light +bedroom light +ceiling fan +thermostat +tv +speaker +front door +garage door +vacuum diff --git a/examples/hass-intent-dataset/base_locale/lb/state.entity b/examples/hass-intent-dataset/base_locale/lb/state.entity new file mode 100644 index 00000000..4faa6b78 --- /dev/null +++ b/examples/hass-intent-dataset/base_locale/lb/state.entity @@ -0,0 +1,6 @@ +on +off +open +closed +locked +unlocked diff --git a/examples/hass-intent-dataset/base_locale/lt/area.entity b/examples/hass-intent-dataset/base_locale/lt/area.entity new file mode 100644 index 00000000..e3f139b8 --- /dev/null +++ b/examples/hass-intent-dataset/base_locale/lt/area.entity @@ -0,0 +1,12 @@ +virtuvė +svetainė +miegamasis +vonios kambarys +biuras +garažas +koridorius +rūsys +valgomasis +sodas +terasa +skalbimo patalpa diff --git a/examples/hass-intent-dataset/base_locale/lt/color.entity b/examples/hass-intent-dataset/base_locale/lt/color.entity new file mode 100644 index 00000000..76285fdf --- /dev/null +++ b/examples/hass-intent-dataset/base_locale/lt/color.entity @@ -0,0 +1,11 @@ +white +black +red +orange +yellow +green +blue +purple +brown +pink +turquoise diff --git a/examples/hass-intent-dataset/base_locale/lt/device_class.entity b/examples/hass-intent-dataset/base_locale/lt/device_class.entity new file mode 100644 index 00000000..8878fb15 --- /dev/null +++ b/examples/hass-intent-dataset/base_locale/lt/device_class.entity @@ -0,0 +1,32 @@ +awning +blind +curtain +door +garage +gate +shade +shutter +window +battery +carbon_monoxide +cold +connectivity +gas +heat +light +lock +moisture +motion +occupancy +opening +plug +power +presence +problem +running +safety +smoke +sound +tamper +update +vibration diff --git a/examples/hass-intent-dataset/base_locale/lt/domain.entity b/examples/hass-intent-dataset/base_locale/lt/domain.entity new file mode 100644 index 00000000..e062292e --- /dev/null +++ b/examples/hass-intent-dataset/base_locale/lt/domain.entity @@ -0,0 +1,19 @@ +light +fan +switch +cover +climate +media_player +sensor +binary_sensor +lock +vacuum +timer +input_boolean +scene +script +automation +weather +camera +humidifier +water_heater diff --git a/examples/hass-intent-dataset/base_locale/lt/floor.entity b/examples/hass-intent-dataset/base_locale/lt/floor.entity new file mode 100644 index 00000000..41f8ddbc --- /dev/null +++ b/examples/hass-intent-dataset/base_locale/lt/floor.entity @@ -0,0 +1,5 @@ +ground floor +first floor +second floor +basement +attic diff --git a/examples/hass-intent-dataset/base_locale/lt/name.entity b/examples/hass-intent-dataset/base_locale/lt/name.entity new file mode 100644 index 00000000..b40f02a9 --- /dev/null +++ b/examples/hass-intent-dataset/base_locale/lt/name.entity @@ -0,0 +1,10 @@ +kitchen light +living room light +bedroom light +ceiling fan +thermostat +tv +speaker +front door +garage door +vacuum diff --git a/examples/hass-intent-dataset/base_locale/lt/state.entity b/examples/hass-intent-dataset/base_locale/lt/state.entity new file mode 100644 index 00000000..4faa6b78 --- /dev/null +++ b/examples/hass-intent-dataset/base_locale/lt/state.entity @@ -0,0 +1,6 @@ +on +off +open +closed +locked +unlocked diff --git a/examples/hass-intent-dataset/base_locale/lv/area.entity b/examples/hass-intent-dataset/base_locale/lv/area.entity new file mode 100644 index 00000000..63418831 --- /dev/null +++ b/examples/hass-intent-dataset/base_locale/lv/area.entity @@ -0,0 +1,12 @@ +virtuve +viesistaba +guļamistaba +vannasistaba +kabinets +garāža +gaitenis +pagrabstāvs +ēdamistaba +dārzs +terase +veļas mazgātava diff --git a/examples/hass-intent-dataset/base_locale/lv/color.entity b/examples/hass-intent-dataset/base_locale/lv/color.entity new file mode 100644 index 00000000..76285fdf --- /dev/null +++ b/examples/hass-intent-dataset/base_locale/lv/color.entity @@ -0,0 +1,11 @@ +white +black +red +orange +yellow +green +blue +purple +brown +pink +turquoise diff --git a/examples/hass-intent-dataset/base_locale/lv/device_class.entity b/examples/hass-intent-dataset/base_locale/lv/device_class.entity new file mode 100644 index 00000000..8878fb15 --- /dev/null +++ b/examples/hass-intent-dataset/base_locale/lv/device_class.entity @@ -0,0 +1,32 @@ +awning +blind +curtain +door +garage +gate +shade +shutter +window +battery +carbon_monoxide +cold +connectivity +gas +heat +light +lock +moisture +motion +occupancy +opening +plug +power +presence +problem +running +safety +smoke +sound +tamper +update +vibration diff --git a/examples/hass-intent-dataset/base_locale/lv/domain.entity b/examples/hass-intent-dataset/base_locale/lv/domain.entity new file mode 100644 index 00000000..e062292e --- /dev/null +++ b/examples/hass-intent-dataset/base_locale/lv/domain.entity @@ -0,0 +1,19 @@ +light +fan +switch +cover +climate +media_player +sensor +binary_sensor +lock +vacuum +timer +input_boolean +scene +script +automation +weather +camera +humidifier +water_heater diff --git a/examples/hass-intent-dataset/base_locale/lv/floor.entity b/examples/hass-intent-dataset/base_locale/lv/floor.entity new file mode 100644 index 00000000..41f8ddbc --- /dev/null +++ b/examples/hass-intent-dataset/base_locale/lv/floor.entity @@ -0,0 +1,5 @@ +ground floor +first floor +second floor +basement +attic diff --git a/examples/hass-intent-dataset/base_locale/lv/name.entity b/examples/hass-intent-dataset/base_locale/lv/name.entity new file mode 100644 index 00000000..b40f02a9 --- /dev/null +++ b/examples/hass-intent-dataset/base_locale/lv/name.entity @@ -0,0 +1,10 @@ +kitchen light +living room light +bedroom light +ceiling fan +thermostat +tv +speaker +front door +garage door +vacuum diff --git a/examples/hass-intent-dataset/base_locale/lv/state.entity b/examples/hass-intent-dataset/base_locale/lv/state.entity new file mode 100644 index 00000000..4faa6b78 --- /dev/null +++ b/examples/hass-intent-dataset/base_locale/lv/state.entity @@ -0,0 +1,6 @@ +on +off +open +closed +locked +unlocked diff --git a/examples/hass-intent-dataset/base_locale/ml/area.entity b/examples/hass-intent-dataset/base_locale/ml/area.entity new file mode 100644 index 00000000..2625cf46 --- /dev/null +++ b/examples/hass-intent-dataset/base_locale/ml/area.entity @@ -0,0 +1,12 @@ +അടുക്കള +ലിവിംഗ് റൂം +ബെഡ്‌റൂം +കുളിമുറി +ഓഫീസ് +ഗാരേജ് +ഇടനാഴി +ബേസ്മെൻറ് +ഡൈനിംഗ് റൂം +പൂന്തോട്ടം +ടെറസ് +ലോണ്ട്രി റൂം diff --git a/examples/hass-intent-dataset/base_locale/ml/color.entity b/examples/hass-intent-dataset/base_locale/ml/color.entity new file mode 100644 index 00000000..76285fdf --- /dev/null +++ b/examples/hass-intent-dataset/base_locale/ml/color.entity @@ -0,0 +1,11 @@ +white +black +red +orange +yellow +green +blue +purple +brown +pink +turquoise diff --git a/examples/hass-intent-dataset/base_locale/ml/device_class.entity b/examples/hass-intent-dataset/base_locale/ml/device_class.entity new file mode 100644 index 00000000..8878fb15 --- /dev/null +++ b/examples/hass-intent-dataset/base_locale/ml/device_class.entity @@ -0,0 +1,32 @@ +awning +blind +curtain +door +garage +gate +shade +shutter +window +battery +carbon_monoxide +cold +connectivity +gas +heat +light +lock +moisture +motion +occupancy +opening +plug +power +presence +problem +running +safety +smoke +sound +tamper +update +vibration diff --git a/examples/hass-intent-dataset/base_locale/ml/domain.entity b/examples/hass-intent-dataset/base_locale/ml/domain.entity new file mode 100644 index 00000000..e062292e --- /dev/null +++ b/examples/hass-intent-dataset/base_locale/ml/domain.entity @@ -0,0 +1,19 @@ +light +fan +switch +cover +climate +media_player +sensor +binary_sensor +lock +vacuum +timer +input_boolean +scene +script +automation +weather +camera +humidifier +water_heater diff --git a/examples/hass-intent-dataset/base_locale/ml/floor.entity b/examples/hass-intent-dataset/base_locale/ml/floor.entity new file mode 100644 index 00000000..41f8ddbc --- /dev/null +++ b/examples/hass-intent-dataset/base_locale/ml/floor.entity @@ -0,0 +1,5 @@ +ground floor +first floor +second floor +basement +attic diff --git a/examples/hass-intent-dataset/base_locale/ml/name.entity b/examples/hass-intent-dataset/base_locale/ml/name.entity new file mode 100644 index 00000000..b40f02a9 --- /dev/null +++ b/examples/hass-intent-dataset/base_locale/ml/name.entity @@ -0,0 +1,10 @@ +kitchen light +living room light +bedroom light +ceiling fan +thermostat +tv +speaker +front door +garage door +vacuum diff --git a/examples/hass-intent-dataset/base_locale/ml/state.entity b/examples/hass-intent-dataset/base_locale/ml/state.entity new file mode 100644 index 00000000..4faa6b78 --- /dev/null +++ b/examples/hass-intent-dataset/base_locale/ml/state.entity @@ -0,0 +1,6 @@ +on +off +open +closed +locked +unlocked diff --git a/examples/hass-intent-dataset/base_locale/mn/area.entity b/examples/hass-intent-dataset/base_locale/mn/area.entity new file mode 100644 index 00000000..c88e1f5d --- /dev/null +++ b/examples/hass-intent-dataset/base_locale/mn/area.entity @@ -0,0 +1,12 @@ +гал тогоо +зочны өрөө +унтлагын өрөө +усанд орох өрөө +оффис +гараж +коридор +суурь +хоолны өрөө +цэцэрлэг +терасс +угаалгын өрөө diff --git a/examples/hass-intent-dataset/base_locale/mn/color.entity b/examples/hass-intent-dataset/base_locale/mn/color.entity new file mode 100644 index 00000000..76285fdf --- /dev/null +++ b/examples/hass-intent-dataset/base_locale/mn/color.entity @@ -0,0 +1,11 @@ +white +black +red +orange +yellow +green +blue +purple +brown +pink +turquoise diff --git a/examples/hass-intent-dataset/base_locale/mn/device_class.entity b/examples/hass-intent-dataset/base_locale/mn/device_class.entity new file mode 100644 index 00000000..8878fb15 --- /dev/null +++ b/examples/hass-intent-dataset/base_locale/mn/device_class.entity @@ -0,0 +1,32 @@ +awning +blind +curtain +door +garage +gate +shade +shutter +window +battery +carbon_monoxide +cold +connectivity +gas +heat +light +lock +moisture +motion +occupancy +opening +plug +power +presence +problem +running +safety +smoke +sound +tamper +update +vibration diff --git a/examples/hass-intent-dataset/base_locale/mn/domain.entity b/examples/hass-intent-dataset/base_locale/mn/domain.entity new file mode 100644 index 00000000..e062292e --- /dev/null +++ b/examples/hass-intent-dataset/base_locale/mn/domain.entity @@ -0,0 +1,19 @@ +light +fan +switch +cover +climate +media_player +sensor +binary_sensor +lock +vacuum +timer +input_boolean +scene +script +automation +weather +camera +humidifier +water_heater diff --git a/examples/hass-intent-dataset/base_locale/mn/floor.entity b/examples/hass-intent-dataset/base_locale/mn/floor.entity new file mode 100644 index 00000000..41f8ddbc --- /dev/null +++ b/examples/hass-intent-dataset/base_locale/mn/floor.entity @@ -0,0 +1,5 @@ +ground floor +first floor +second floor +basement +attic diff --git a/examples/hass-intent-dataset/base_locale/mn/name.entity b/examples/hass-intent-dataset/base_locale/mn/name.entity new file mode 100644 index 00000000..b40f02a9 --- /dev/null +++ b/examples/hass-intent-dataset/base_locale/mn/name.entity @@ -0,0 +1,10 @@ +kitchen light +living room light +bedroom light +ceiling fan +thermostat +tv +speaker +front door +garage door +vacuum diff --git a/examples/hass-intent-dataset/base_locale/mn/state.entity b/examples/hass-intent-dataset/base_locale/mn/state.entity new file mode 100644 index 00000000..4faa6b78 --- /dev/null +++ b/examples/hass-intent-dataset/base_locale/mn/state.entity @@ -0,0 +1,6 @@ +on +off +open +closed +locked +unlocked diff --git a/examples/hass-intent-dataset/base_locale/mr/area.entity b/examples/hass-intent-dataset/base_locale/mr/area.entity new file mode 100644 index 00000000..868ce130 --- /dev/null +++ b/examples/hass-intent-dataset/base_locale/mr/area.entity @@ -0,0 +1,12 @@ +स्वयंपाकघर +दिवाणखाना +बेडरूम +स्नानगृह +कार्यालय +गॅरेज +कॉरिडॉर +तळघर +जेवणाची खोली +बाग +टेरेस +लॉन्ड्री रूम diff --git a/examples/hass-intent-dataset/base_locale/mr/color.entity b/examples/hass-intent-dataset/base_locale/mr/color.entity new file mode 100644 index 00000000..76285fdf --- /dev/null +++ b/examples/hass-intent-dataset/base_locale/mr/color.entity @@ -0,0 +1,11 @@ +white +black +red +orange +yellow +green +blue +purple +brown +pink +turquoise diff --git a/examples/hass-intent-dataset/base_locale/mr/device_class.entity b/examples/hass-intent-dataset/base_locale/mr/device_class.entity new file mode 100644 index 00000000..8878fb15 --- /dev/null +++ b/examples/hass-intent-dataset/base_locale/mr/device_class.entity @@ -0,0 +1,32 @@ +awning +blind +curtain +door +garage +gate +shade +shutter +window +battery +carbon_monoxide +cold +connectivity +gas +heat +light +lock +moisture +motion +occupancy +opening +plug +power +presence +problem +running +safety +smoke +sound +tamper +update +vibration diff --git a/examples/hass-intent-dataset/base_locale/mr/domain.entity b/examples/hass-intent-dataset/base_locale/mr/domain.entity new file mode 100644 index 00000000..e062292e --- /dev/null +++ b/examples/hass-intent-dataset/base_locale/mr/domain.entity @@ -0,0 +1,19 @@ +light +fan +switch +cover +climate +media_player +sensor +binary_sensor +lock +vacuum +timer +input_boolean +scene +script +automation +weather +camera +humidifier +water_heater diff --git a/examples/hass-intent-dataset/base_locale/mr/floor.entity b/examples/hass-intent-dataset/base_locale/mr/floor.entity new file mode 100644 index 00000000..41f8ddbc --- /dev/null +++ b/examples/hass-intent-dataset/base_locale/mr/floor.entity @@ -0,0 +1,5 @@ +ground floor +first floor +second floor +basement +attic diff --git a/examples/hass-intent-dataset/base_locale/mr/name.entity b/examples/hass-intent-dataset/base_locale/mr/name.entity new file mode 100644 index 00000000..b40f02a9 --- /dev/null +++ b/examples/hass-intent-dataset/base_locale/mr/name.entity @@ -0,0 +1,10 @@ +kitchen light +living room light +bedroom light +ceiling fan +thermostat +tv +speaker +front door +garage door +vacuum diff --git a/examples/hass-intent-dataset/base_locale/mr/state.entity b/examples/hass-intent-dataset/base_locale/mr/state.entity new file mode 100644 index 00000000..4faa6b78 --- /dev/null +++ b/examples/hass-intent-dataset/base_locale/mr/state.entity @@ -0,0 +1,6 @@ +on +off +open +closed +locked +unlocked diff --git a/examples/hass-intent-dataset/base_locale/nb/area.entity b/examples/hass-intent-dataset/base_locale/nb/area.entity new file mode 100644 index 00000000..0e34bacf --- /dev/null +++ b/examples/hass-intent-dataset/base_locale/nb/area.entity @@ -0,0 +1,12 @@ +kjøkken +stue +soverom +bad +kontor +garasje +gang +kjeller +spisestue +hage +terrasse +vaskerom diff --git a/examples/hass-intent-dataset/base_locale/nb/color.entity b/examples/hass-intent-dataset/base_locale/nb/color.entity new file mode 100644 index 00000000..697ebebd --- /dev/null +++ b/examples/hass-intent-dataset/base_locale/nb/color.entity @@ -0,0 +1,11 @@ +hvit +svart +rød +oransje +gul +grønn +blå +lilla +brun +rosa +turkis diff --git a/examples/hass-intent-dataset/base_locale/nb/device_class.entity b/examples/hass-intent-dataset/base_locale/nb/device_class.entity new file mode 100644 index 00000000..8878fb15 --- /dev/null +++ b/examples/hass-intent-dataset/base_locale/nb/device_class.entity @@ -0,0 +1,32 @@ +awning +blind +curtain +door +garage +gate +shade +shutter +window +battery +carbon_monoxide +cold +connectivity +gas +heat +light +lock +moisture +motion +occupancy +opening +plug +power +presence +problem +running +safety +smoke +sound +tamper +update +vibration diff --git a/examples/hass-intent-dataset/base_locale/nb/domain.entity b/examples/hass-intent-dataset/base_locale/nb/domain.entity new file mode 100644 index 00000000..e062292e --- /dev/null +++ b/examples/hass-intent-dataset/base_locale/nb/domain.entity @@ -0,0 +1,19 @@ +light +fan +switch +cover +climate +media_player +sensor +binary_sensor +lock +vacuum +timer +input_boolean +scene +script +automation +weather +camera +humidifier +water_heater diff --git a/examples/hass-intent-dataset/base_locale/nb/floor.entity b/examples/hass-intent-dataset/base_locale/nb/floor.entity new file mode 100644 index 00000000..41f8ddbc --- /dev/null +++ b/examples/hass-intent-dataset/base_locale/nb/floor.entity @@ -0,0 +1,5 @@ +ground floor +first floor +second floor +basement +attic diff --git a/examples/hass-intent-dataset/base_locale/nb/name.entity b/examples/hass-intent-dataset/base_locale/nb/name.entity new file mode 100644 index 00000000..b40f02a9 --- /dev/null +++ b/examples/hass-intent-dataset/base_locale/nb/name.entity @@ -0,0 +1,10 @@ +kitchen light +living room light +bedroom light +ceiling fan +thermostat +tv +speaker +front door +garage door +vacuum diff --git a/examples/hass-intent-dataset/base_locale/nb/state.entity b/examples/hass-intent-dataset/base_locale/nb/state.entity new file mode 100644 index 00000000..9c15e655 --- /dev/null +++ b/examples/hass-intent-dataset/base_locale/nb/state.entity @@ -0,0 +1,6 @@ +på +av +åpen +lukket +låst +opplåst diff --git a/examples/hass-intent-dataset/base_locale/ne/area.entity b/examples/hass-intent-dataset/base_locale/ne/area.entity new file mode 100644 index 00000000..f80dc0ff --- /dev/null +++ b/examples/hass-intent-dataset/base_locale/ne/area.entity @@ -0,0 +1,12 @@ +भान्सा +बैठक कोठा +सुत्ने कोठा +बाथरूम +अफिस +ग्यारेज +कोरिडोर +भूमिगत +खाना कोठा +बगैंचा +टेरेस +लुगा धुने कोठा diff --git a/examples/hass-intent-dataset/base_locale/ne/color.entity b/examples/hass-intent-dataset/base_locale/ne/color.entity new file mode 100644 index 00000000..76285fdf --- /dev/null +++ b/examples/hass-intent-dataset/base_locale/ne/color.entity @@ -0,0 +1,11 @@ +white +black +red +orange +yellow +green +blue +purple +brown +pink +turquoise diff --git a/examples/hass-intent-dataset/base_locale/ne/device_class.entity b/examples/hass-intent-dataset/base_locale/ne/device_class.entity new file mode 100644 index 00000000..8878fb15 --- /dev/null +++ b/examples/hass-intent-dataset/base_locale/ne/device_class.entity @@ -0,0 +1,32 @@ +awning +blind +curtain +door +garage +gate +shade +shutter +window +battery +carbon_monoxide +cold +connectivity +gas +heat +light +lock +moisture +motion +occupancy +opening +plug +power +presence +problem +running +safety +smoke +sound +tamper +update +vibration diff --git a/examples/hass-intent-dataset/base_locale/ne/domain.entity b/examples/hass-intent-dataset/base_locale/ne/domain.entity new file mode 100644 index 00000000..e062292e --- /dev/null +++ b/examples/hass-intent-dataset/base_locale/ne/domain.entity @@ -0,0 +1,19 @@ +light +fan +switch +cover +climate +media_player +sensor +binary_sensor +lock +vacuum +timer +input_boolean +scene +script +automation +weather +camera +humidifier +water_heater diff --git a/examples/hass-intent-dataset/base_locale/ne/floor.entity b/examples/hass-intent-dataset/base_locale/ne/floor.entity new file mode 100644 index 00000000..41f8ddbc --- /dev/null +++ b/examples/hass-intent-dataset/base_locale/ne/floor.entity @@ -0,0 +1,5 @@ +ground floor +first floor +second floor +basement +attic diff --git a/examples/hass-intent-dataset/base_locale/ne/name.entity b/examples/hass-intent-dataset/base_locale/ne/name.entity new file mode 100644 index 00000000..b40f02a9 --- /dev/null +++ b/examples/hass-intent-dataset/base_locale/ne/name.entity @@ -0,0 +1,10 @@ +kitchen light +living room light +bedroom light +ceiling fan +thermostat +tv +speaker +front door +garage door +vacuum diff --git a/examples/hass-intent-dataset/base_locale/ne/state.entity b/examples/hass-intent-dataset/base_locale/ne/state.entity new file mode 100644 index 00000000..4faa6b78 --- /dev/null +++ b/examples/hass-intent-dataset/base_locale/ne/state.entity @@ -0,0 +1,6 @@ +on +off +open +closed +locked +unlocked diff --git a/examples/hass-intent-dataset/base_locale/nl/area.entity b/examples/hass-intent-dataset/base_locale/nl/area.entity new file mode 100644 index 00000000..dc4b8693 --- /dev/null +++ b/examples/hass-intent-dataset/base_locale/nl/area.entity @@ -0,0 +1,12 @@ +keuken +woonkamer +slaapkamer +badkamer +kantoor +garage +gang +kelder +eetkamer +tuin +terras +wasruimte diff --git a/examples/hass-intent-dataset/base_locale/nl/color.entity b/examples/hass-intent-dataset/base_locale/nl/color.entity new file mode 100644 index 00000000..f512c3f2 --- /dev/null +++ b/examples/hass-intent-dataset/base_locale/nl/color.entity @@ -0,0 +1,11 @@ +wit +zwart +rood +oranje +geel +groen +blauw +paars +bruin +roze +turquoise diff --git a/examples/hass-intent-dataset/base_locale/nl/device_class.entity b/examples/hass-intent-dataset/base_locale/nl/device_class.entity new file mode 100644 index 00000000..8878fb15 --- /dev/null +++ b/examples/hass-intent-dataset/base_locale/nl/device_class.entity @@ -0,0 +1,32 @@ +awning +blind +curtain +door +garage +gate +shade +shutter +window +battery +carbon_monoxide +cold +connectivity +gas +heat +light +lock +moisture +motion +occupancy +opening +plug +power +presence +problem +running +safety +smoke +sound +tamper +update +vibration diff --git a/examples/hass-intent-dataset/base_locale/nl/domain.entity b/examples/hass-intent-dataset/base_locale/nl/domain.entity new file mode 100644 index 00000000..e062292e --- /dev/null +++ b/examples/hass-intent-dataset/base_locale/nl/domain.entity @@ -0,0 +1,19 @@ +light +fan +switch +cover +climate +media_player +sensor +binary_sensor +lock +vacuum +timer +input_boolean +scene +script +automation +weather +camera +humidifier +water_heater diff --git a/examples/hass-intent-dataset/base_locale/nl/floor.entity b/examples/hass-intent-dataset/base_locale/nl/floor.entity new file mode 100644 index 00000000..77686810 --- /dev/null +++ b/examples/hass-intent-dataset/base_locale/nl/floor.entity @@ -0,0 +1,5 @@ +begane grond +eerste verdieping +tweede verdieping +kelder +zolder diff --git a/examples/hass-intent-dataset/base_locale/nl/name.entity b/examples/hass-intent-dataset/base_locale/nl/name.entity new file mode 100644 index 00000000..2606c7c9 --- /dev/null +++ b/examples/hass-intent-dataset/base_locale/nl/name.entity @@ -0,0 +1,10 @@ +keukenverlichting +woonkamerverlichting +slaapkamerverlichting +plafondventilator +thermostaat +televisie +luidspreker +voordeur +garagedeur +stofzuiger diff --git a/examples/hass-intent-dataset/base_locale/nl/state.entity b/examples/hass-intent-dataset/base_locale/nl/state.entity new file mode 100644 index 00000000..546f4e7e --- /dev/null +++ b/examples/hass-intent-dataset/base_locale/nl/state.entity @@ -0,0 +1,6 @@ +aan +uit +open +gesloten +vergrendeld +ontgrendeld diff --git a/examples/hass-intent-dataset/base_locale/pa/area.entity b/examples/hass-intent-dataset/base_locale/pa/area.entity new file mode 100644 index 00000000..6fabd683 --- /dev/null +++ b/examples/hass-intent-dataset/base_locale/pa/area.entity @@ -0,0 +1,12 @@ +ਰਸੋਈ +ਲਿਵਿੰਗ ਰੂਮ +ਬੈੱਡਰੂਮ +ਬਾਥਰੂਮ +ਦਫਤਰ +ਗੈਰਾਜ +ਕੋਰੀਡੋਰ +ਬੇਸਮੈਂਟ +ਡਾਇਨਿੰਗ ਰੂਮ +ਬਾਗ +ਛੱਤ +ਲਾਂਡਰੀ ਰੂਮ diff --git a/examples/hass-intent-dataset/base_locale/pa/color.entity b/examples/hass-intent-dataset/base_locale/pa/color.entity new file mode 100644 index 00000000..76285fdf --- /dev/null +++ b/examples/hass-intent-dataset/base_locale/pa/color.entity @@ -0,0 +1,11 @@ +white +black +red +orange +yellow +green +blue +purple +brown +pink +turquoise diff --git a/examples/hass-intent-dataset/base_locale/pa/device_class.entity b/examples/hass-intent-dataset/base_locale/pa/device_class.entity new file mode 100644 index 00000000..8878fb15 --- /dev/null +++ b/examples/hass-intent-dataset/base_locale/pa/device_class.entity @@ -0,0 +1,32 @@ +awning +blind +curtain +door +garage +gate +shade +shutter +window +battery +carbon_monoxide +cold +connectivity +gas +heat +light +lock +moisture +motion +occupancy +opening +plug +power +presence +problem +running +safety +smoke +sound +tamper +update +vibration diff --git a/examples/hass-intent-dataset/base_locale/pa/domain.entity b/examples/hass-intent-dataset/base_locale/pa/domain.entity new file mode 100644 index 00000000..e062292e --- /dev/null +++ b/examples/hass-intent-dataset/base_locale/pa/domain.entity @@ -0,0 +1,19 @@ +light +fan +switch +cover +climate +media_player +sensor +binary_sensor +lock +vacuum +timer +input_boolean +scene +script +automation +weather +camera +humidifier +water_heater diff --git a/examples/hass-intent-dataset/base_locale/pa/floor.entity b/examples/hass-intent-dataset/base_locale/pa/floor.entity new file mode 100644 index 00000000..41f8ddbc --- /dev/null +++ b/examples/hass-intent-dataset/base_locale/pa/floor.entity @@ -0,0 +1,5 @@ +ground floor +first floor +second floor +basement +attic diff --git a/examples/hass-intent-dataset/base_locale/pa/name.entity b/examples/hass-intent-dataset/base_locale/pa/name.entity new file mode 100644 index 00000000..b40f02a9 --- /dev/null +++ b/examples/hass-intent-dataset/base_locale/pa/name.entity @@ -0,0 +1,10 @@ +kitchen light +living room light +bedroom light +ceiling fan +thermostat +tv +speaker +front door +garage door +vacuum diff --git a/examples/hass-intent-dataset/base_locale/pa/state.entity b/examples/hass-intent-dataset/base_locale/pa/state.entity new file mode 100644 index 00000000..4faa6b78 --- /dev/null +++ b/examples/hass-intent-dataset/base_locale/pa/state.entity @@ -0,0 +1,6 @@ +on +off +open +closed +locked +unlocked diff --git a/examples/hass-intent-dataset/base_locale/pl/area.entity b/examples/hass-intent-dataset/base_locale/pl/area.entity new file mode 100644 index 00000000..3e9ba7f2 --- /dev/null +++ b/examples/hass-intent-dataset/base_locale/pl/area.entity @@ -0,0 +1,12 @@ +kuchnia +salon +sypialnia +łazienka +biuro +garaż +korytarz +piwnica +jadalnia +ogród +taras +pralnia diff --git a/examples/hass-intent-dataset/base_locale/pl/color.entity b/examples/hass-intent-dataset/base_locale/pl/color.entity new file mode 100644 index 00000000..591d9846 --- /dev/null +++ b/examples/hass-intent-dataset/base_locale/pl/color.entity @@ -0,0 +1,11 @@ +biały +czarny +czerwony +pomarańczowy +żółty +zielony +niebieski +fioletowy +brązowy +różowy +turkusowy diff --git a/examples/hass-intent-dataset/base_locale/pl/device_class.entity b/examples/hass-intent-dataset/base_locale/pl/device_class.entity new file mode 100644 index 00000000..8878fb15 --- /dev/null +++ b/examples/hass-intent-dataset/base_locale/pl/device_class.entity @@ -0,0 +1,32 @@ +awning +blind +curtain +door +garage +gate +shade +shutter +window +battery +carbon_monoxide +cold +connectivity +gas +heat +light +lock +moisture +motion +occupancy +opening +plug +power +presence +problem +running +safety +smoke +sound +tamper +update +vibration diff --git a/examples/hass-intent-dataset/base_locale/pl/domain.entity b/examples/hass-intent-dataset/base_locale/pl/domain.entity new file mode 100644 index 00000000..e062292e --- /dev/null +++ b/examples/hass-intent-dataset/base_locale/pl/domain.entity @@ -0,0 +1,19 @@ +light +fan +switch +cover +climate +media_player +sensor +binary_sensor +lock +vacuum +timer +input_boolean +scene +script +automation +weather +camera +humidifier +water_heater diff --git a/examples/hass-intent-dataset/base_locale/pl/floor.entity b/examples/hass-intent-dataset/base_locale/pl/floor.entity new file mode 100644 index 00000000..f65f0e2c --- /dev/null +++ b/examples/hass-intent-dataset/base_locale/pl/floor.entity @@ -0,0 +1,5 @@ +parter +pierwsze piętro +drugie piętro +piwnica +strych diff --git a/examples/hass-intent-dataset/base_locale/pl/name.entity b/examples/hass-intent-dataset/base_locale/pl/name.entity new file mode 100644 index 00000000..7d0fcc1f --- /dev/null +++ b/examples/hass-intent-dataset/base_locale/pl/name.entity @@ -0,0 +1,10 @@ +światło w kuchni +światło w salonie +światło w sypialni +wentylator sufitowy +termostat +telewizor +głośnik +drzwi wejściowe +drzwi garażowe +odkurzacz diff --git a/examples/hass-intent-dataset/base_locale/pl/state.entity b/examples/hass-intent-dataset/base_locale/pl/state.entity new file mode 100644 index 00000000..dbc7ba1b --- /dev/null +++ b/examples/hass-intent-dataset/base_locale/pl/state.entity @@ -0,0 +1,6 @@ +włączony +wyłączony +otwarty +zamknięty +zamknięty +otwarty diff --git a/examples/hass-intent-dataset/base_locale/pt-BR/area.entity b/examples/hass-intent-dataset/base_locale/pt-BR/area.entity new file mode 100644 index 00000000..ac2f18f9 --- /dev/null +++ b/examples/hass-intent-dataset/base_locale/pt-BR/area.entity @@ -0,0 +1,12 @@ +cozinha +sala +quarto +banheiro +escritório +garagem +corredor +porão +sala de jantar +jardim +terraço +lavanderia diff --git a/examples/hass-intent-dataset/base_locale/pt-BR/color.entity b/examples/hass-intent-dataset/base_locale/pt-BR/color.entity new file mode 100644 index 00000000..72908fc6 --- /dev/null +++ b/examples/hass-intent-dataset/base_locale/pt-BR/color.entity @@ -0,0 +1,11 @@ +branco +preto +vermelho +laranja +amarelo +verde +azul +roxo +marrom +rosa +turquesa diff --git a/examples/hass-intent-dataset/base_locale/pt-BR/device_class.entity b/examples/hass-intent-dataset/base_locale/pt-BR/device_class.entity new file mode 100644 index 00000000..8878fb15 --- /dev/null +++ b/examples/hass-intent-dataset/base_locale/pt-BR/device_class.entity @@ -0,0 +1,32 @@ +awning +blind +curtain +door +garage +gate +shade +shutter +window +battery +carbon_monoxide +cold +connectivity +gas +heat +light +lock +moisture +motion +occupancy +opening +plug +power +presence +problem +running +safety +smoke +sound +tamper +update +vibration diff --git a/examples/hass-intent-dataset/base_locale/pt-BR/domain.entity b/examples/hass-intent-dataset/base_locale/pt-BR/domain.entity new file mode 100644 index 00000000..e062292e --- /dev/null +++ b/examples/hass-intent-dataset/base_locale/pt-BR/domain.entity @@ -0,0 +1,19 @@ +light +fan +switch +cover +climate +media_player +sensor +binary_sensor +lock +vacuum +timer +input_boolean +scene +script +automation +weather +camera +humidifier +water_heater diff --git a/examples/hass-intent-dataset/base_locale/pt-BR/floor.entity b/examples/hass-intent-dataset/base_locale/pt-BR/floor.entity new file mode 100644 index 00000000..91837528 --- /dev/null +++ b/examples/hass-intent-dataset/base_locale/pt-BR/floor.entity @@ -0,0 +1,5 @@ +térreo +primeiro andar +segundo andar +porão +sótão diff --git a/examples/hass-intent-dataset/base_locale/pt-BR/name.entity b/examples/hass-intent-dataset/base_locale/pt-BR/name.entity new file mode 100644 index 00000000..adaab0dd --- /dev/null +++ b/examples/hass-intent-dataset/base_locale/pt-BR/name.entity @@ -0,0 +1,10 @@ +luz da cozinha +luz da sala +luz do quarto +ventilador de teto +termostato +televisão +caixa de som +porta da frente +porta da garagem +aspirador diff --git a/examples/hass-intent-dataset/base_locale/pt-BR/state.entity b/examples/hass-intent-dataset/base_locale/pt-BR/state.entity new file mode 100644 index 00000000..e0d55528 --- /dev/null +++ b/examples/hass-intent-dataset/base_locale/pt-BR/state.entity @@ -0,0 +1,6 @@ +ligado +desligado +aberto +fechado +trancado +destrancado diff --git a/examples/hass-intent-dataset/base_locale/pt/area.entity b/examples/hass-intent-dataset/base_locale/pt/area.entity new file mode 100644 index 00000000..49fab10c --- /dev/null +++ b/examples/hass-intent-dataset/base_locale/pt/area.entity @@ -0,0 +1,12 @@ +cozinha +sala de estar +quarto +casa de banho +escritório +garagem +corredor +cave +sala de jantar +jardim +terraço +lavandaria diff --git a/examples/hass-intent-dataset/base_locale/pt/color.entity b/examples/hass-intent-dataset/base_locale/pt/color.entity new file mode 100644 index 00000000..7058af80 --- /dev/null +++ b/examples/hass-intent-dataset/base_locale/pt/color.entity @@ -0,0 +1,11 @@ +branco +preto +vermelho +laranja +amarelo +verde +azul +roxo +castanho +rosa +turquesa diff --git a/examples/hass-intent-dataset/base_locale/pt/device_class.entity b/examples/hass-intent-dataset/base_locale/pt/device_class.entity new file mode 100644 index 00000000..8878fb15 --- /dev/null +++ b/examples/hass-intent-dataset/base_locale/pt/device_class.entity @@ -0,0 +1,32 @@ +awning +blind +curtain +door +garage +gate +shade +shutter +window +battery +carbon_monoxide +cold +connectivity +gas +heat +light +lock +moisture +motion +occupancy +opening +plug +power +presence +problem +running +safety +smoke +sound +tamper +update +vibration diff --git a/examples/hass-intent-dataset/base_locale/pt/domain.entity b/examples/hass-intent-dataset/base_locale/pt/domain.entity new file mode 100644 index 00000000..e062292e --- /dev/null +++ b/examples/hass-intent-dataset/base_locale/pt/domain.entity @@ -0,0 +1,19 @@ +light +fan +switch +cover +climate +media_player +sensor +binary_sensor +lock +vacuum +timer +input_boolean +scene +script +automation +weather +camera +humidifier +water_heater diff --git a/examples/hass-intent-dataset/base_locale/pt/floor.entity b/examples/hass-intent-dataset/base_locale/pt/floor.entity new file mode 100644 index 00000000..6a5a6ca0 --- /dev/null +++ b/examples/hass-intent-dataset/base_locale/pt/floor.entity @@ -0,0 +1,5 @@ +rés do chão +primeiro andar +segundo andar +cave +sótão diff --git a/examples/hass-intent-dataset/base_locale/pt/name.entity b/examples/hass-intent-dataset/base_locale/pt/name.entity new file mode 100644 index 00000000..b99a3ca8 --- /dev/null +++ b/examples/hass-intent-dataset/base_locale/pt/name.entity @@ -0,0 +1,10 @@ +luz da cozinha +luz da sala +luz do quarto +ventilador de teto +termostato +televisão +altifalante +porta da frente +porta da garagem +aspirador diff --git a/examples/hass-intent-dataset/base_locale/pt/state.entity b/examples/hass-intent-dataset/base_locale/pt/state.entity new file mode 100644 index 00000000..e0d55528 --- /dev/null +++ b/examples/hass-intent-dataset/base_locale/pt/state.entity @@ -0,0 +1,6 @@ +ligado +desligado +aberto +fechado +trancado +destrancado diff --git a/examples/hass-intent-dataset/base_locale/ro/area.entity b/examples/hass-intent-dataset/base_locale/ro/area.entity new file mode 100644 index 00000000..cf0f9b28 --- /dev/null +++ b/examples/hass-intent-dataset/base_locale/ro/area.entity @@ -0,0 +1,12 @@ +bucătărie +sufragerie +dormitor +baie +birou +garaj +hol +subsol +sală de mese +grădină +terasă +spălătorie diff --git a/examples/hass-intent-dataset/base_locale/ro/color.entity b/examples/hass-intent-dataset/base_locale/ro/color.entity new file mode 100644 index 00000000..76285fdf --- /dev/null +++ b/examples/hass-intent-dataset/base_locale/ro/color.entity @@ -0,0 +1,11 @@ +white +black +red +orange +yellow +green +blue +purple +brown +pink +turquoise diff --git a/examples/hass-intent-dataset/base_locale/ro/device_class.entity b/examples/hass-intent-dataset/base_locale/ro/device_class.entity new file mode 100644 index 00000000..8878fb15 --- /dev/null +++ b/examples/hass-intent-dataset/base_locale/ro/device_class.entity @@ -0,0 +1,32 @@ +awning +blind +curtain +door +garage +gate +shade +shutter +window +battery +carbon_monoxide +cold +connectivity +gas +heat +light +lock +moisture +motion +occupancy +opening +plug +power +presence +problem +running +safety +smoke +sound +tamper +update +vibration diff --git a/examples/hass-intent-dataset/base_locale/ro/domain.entity b/examples/hass-intent-dataset/base_locale/ro/domain.entity new file mode 100644 index 00000000..e062292e --- /dev/null +++ b/examples/hass-intent-dataset/base_locale/ro/domain.entity @@ -0,0 +1,19 @@ +light +fan +switch +cover +climate +media_player +sensor +binary_sensor +lock +vacuum +timer +input_boolean +scene +script +automation +weather +camera +humidifier +water_heater diff --git a/examples/hass-intent-dataset/base_locale/ro/floor.entity b/examples/hass-intent-dataset/base_locale/ro/floor.entity new file mode 100644 index 00000000..41f8ddbc --- /dev/null +++ b/examples/hass-intent-dataset/base_locale/ro/floor.entity @@ -0,0 +1,5 @@ +ground floor +first floor +second floor +basement +attic diff --git a/examples/hass-intent-dataset/base_locale/ro/name.entity b/examples/hass-intent-dataset/base_locale/ro/name.entity new file mode 100644 index 00000000..b40f02a9 --- /dev/null +++ b/examples/hass-intent-dataset/base_locale/ro/name.entity @@ -0,0 +1,10 @@ +kitchen light +living room light +bedroom light +ceiling fan +thermostat +tv +speaker +front door +garage door +vacuum diff --git a/examples/hass-intent-dataset/base_locale/ro/state.entity b/examples/hass-intent-dataset/base_locale/ro/state.entity new file mode 100644 index 00000000..4faa6b78 --- /dev/null +++ b/examples/hass-intent-dataset/base_locale/ro/state.entity @@ -0,0 +1,6 @@ +on +off +open +closed +locked +unlocked diff --git a/examples/hass-intent-dataset/base_locale/ru/area.entity b/examples/hass-intent-dataset/base_locale/ru/area.entity new file mode 100644 index 00000000..cd4bd97c --- /dev/null +++ b/examples/hass-intent-dataset/base_locale/ru/area.entity @@ -0,0 +1,12 @@ +кухня +гостиная +спальня +ванная +офис +гараж +коридор +подвал +столовая +сад +терраса +прачечная diff --git a/examples/hass-intent-dataset/base_locale/ru/color.entity b/examples/hass-intent-dataset/base_locale/ru/color.entity new file mode 100644 index 00000000..2d66ccd9 --- /dev/null +++ b/examples/hass-intent-dataset/base_locale/ru/color.entity @@ -0,0 +1,11 @@ +белый +чёрный +красный +оранжевый +жёлтый +зелёный +синий +фиолетовый +коричневый +розовый +бирюзовый diff --git a/examples/hass-intent-dataset/base_locale/ru/device_class.entity b/examples/hass-intent-dataset/base_locale/ru/device_class.entity new file mode 100644 index 00000000..8878fb15 --- /dev/null +++ b/examples/hass-intent-dataset/base_locale/ru/device_class.entity @@ -0,0 +1,32 @@ +awning +blind +curtain +door +garage +gate +shade +shutter +window +battery +carbon_monoxide +cold +connectivity +gas +heat +light +lock +moisture +motion +occupancy +opening +plug +power +presence +problem +running +safety +smoke +sound +tamper +update +vibration diff --git a/examples/hass-intent-dataset/base_locale/ru/domain.entity b/examples/hass-intent-dataset/base_locale/ru/domain.entity new file mode 100644 index 00000000..e062292e --- /dev/null +++ b/examples/hass-intent-dataset/base_locale/ru/domain.entity @@ -0,0 +1,19 @@ +light +fan +switch +cover +climate +media_player +sensor +binary_sensor +lock +vacuum +timer +input_boolean +scene +script +automation +weather +camera +humidifier +water_heater diff --git a/examples/hass-intent-dataset/base_locale/ru/floor.entity b/examples/hass-intent-dataset/base_locale/ru/floor.entity new file mode 100644 index 00000000..baca3e78 --- /dev/null +++ b/examples/hass-intent-dataset/base_locale/ru/floor.entity @@ -0,0 +1,5 @@ +первый этаж +второй этаж +третий этаж +подвал +чердак diff --git a/examples/hass-intent-dataset/base_locale/ru/name.entity b/examples/hass-intent-dataset/base_locale/ru/name.entity new file mode 100644 index 00000000..b28aa861 --- /dev/null +++ b/examples/hass-intent-dataset/base_locale/ru/name.entity @@ -0,0 +1,10 @@ +свет на кухне +свет в гостиной +свет в спальне +потолочный вентилятор +термостат +телевизор +колонка +входная дверь +дверь гаража +пылесос diff --git a/examples/hass-intent-dataset/base_locale/ru/state.entity b/examples/hass-intent-dataset/base_locale/ru/state.entity new file mode 100644 index 00000000..9e4d9515 --- /dev/null +++ b/examples/hass-intent-dataset/base_locale/ru/state.entity @@ -0,0 +1,6 @@ +включено +выключено +открыто +закрыто +заблокировано +разблокировано diff --git a/examples/hass-intent-dataset/base_locale/sk/area.entity b/examples/hass-intent-dataset/base_locale/sk/area.entity new file mode 100644 index 00000000..5420e843 --- /dev/null +++ b/examples/hass-intent-dataset/base_locale/sk/area.entity @@ -0,0 +1,12 @@ +kuchyňa +obývačka +spálňa +kúpeľňa +kancelária +garáž +chodba +pivnica +jedáleň +záhrada +terasa +práčovňa diff --git a/examples/hass-intent-dataset/base_locale/sk/color.entity b/examples/hass-intent-dataset/base_locale/sk/color.entity new file mode 100644 index 00000000..76285fdf --- /dev/null +++ b/examples/hass-intent-dataset/base_locale/sk/color.entity @@ -0,0 +1,11 @@ +white +black +red +orange +yellow +green +blue +purple +brown +pink +turquoise diff --git a/examples/hass-intent-dataset/base_locale/sk/device_class.entity b/examples/hass-intent-dataset/base_locale/sk/device_class.entity new file mode 100644 index 00000000..8878fb15 --- /dev/null +++ b/examples/hass-intent-dataset/base_locale/sk/device_class.entity @@ -0,0 +1,32 @@ +awning +blind +curtain +door +garage +gate +shade +shutter +window +battery +carbon_monoxide +cold +connectivity +gas +heat +light +lock +moisture +motion +occupancy +opening +plug +power +presence +problem +running +safety +smoke +sound +tamper +update +vibration diff --git a/examples/hass-intent-dataset/base_locale/sk/domain.entity b/examples/hass-intent-dataset/base_locale/sk/domain.entity new file mode 100644 index 00000000..e062292e --- /dev/null +++ b/examples/hass-intent-dataset/base_locale/sk/domain.entity @@ -0,0 +1,19 @@ +light +fan +switch +cover +climate +media_player +sensor +binary_sensor +lock +vacuum +timer +input_boolean +scene +script +automation +weather +camera +humidifier +water_heater diff --git a/examples/hass-intent-dataset/base_locale/sk/floor.entity b/examples/hass-intent-dataset/base_locale/sk/floor.entity new file mode 100644 index 00000000..41f8ddbc --- /dev/null +++ b/examples/hass-intent-dataset/base_locale/sk/floor.entity @@ -0,0 +1,5 @@ +ground floor +first floor +second floor +basement +attic diff --git a/examples/hass-intent-dataset/base_locale/sk/name.entity b/examples/hass-intent-dataset/base_locale/sk/name.entity new file mode 100644 index 00000000..b40f02a9 --- /dev/null +++ b/examples/hass-intent-dataset/base_locale/sk/name.entity @@ -0,0 +1,10 @@ +kitchen light +living room light +bedroom light +ceiling fan +thermostat +tv +speaker +front door +garage door +vacuum diff --git a/examples/hass-intent-dataset/base_locale/sk/state.entity b/examples/hass-intent-dataset/base_locale/sk/state.entity new file mode 100644 index 00000000..4faa6b78 --- /dev/null +++ b/examples/hass-intent-dataset/base_locale/sk/state.entity @@ -0,0 +1,6 @@ +on +off +open +closed +locked +unlocked diff --git a/examples/hass-intent-dataset/base_locale/sl/area.entity b/examples/hass-intent-dataset/base_locale/sl/area.entity new file mode 100644 index 00000000..7d59b625 --- /dev/null +++ b/examples/hass-intent-dataset/base_locale/sl/area.entity @@ -0,0 +1,12 @@ +kuhinja +dnevna soba +spalnica +kopalnica +pisarna +garaža +hodnik +klet +jedilnica +vrt +terasa +pralnica diff --git a/examples/hass-intent-dataset/base_locale/sl/color.entity b/examples/hass-intent-dataset/base_locale/sl/color.entity new file mode 100644 index 00000000..76285fdf --- /dev/null +++ b/examples/hass-intent-dataset/base_locale/sl/color.entity @@ -0,0 +1,11 @@ +white +black +red +orange +yellow +green +blue +purple +brown +pink +turquoise diff --git a/examples/hass-intent-dataset/base_locale/sl/device_class.entity b/examples/hass-intent-dataset/base_locale/sl/device_class.entity new file mode 100644 index 00000000..8878fb15 --- /dev/null +++ b/examples/hass-intent-dataset/base_locale/sl/device_class.entity @@ -0,0 +1,32 @@ +awning +blind +curtain +door +garage +gate +shade +shutter +window +battery +carbon_monoxide +cold +connectivity +gas +heat +light +lock +moisture +motion +occupancy +opening +plug +power +presence +problem +running +safety +smoke +sound +tamper +update +vibration diff --git a/examples/hass-intent-dataset/base_locale/sl/domain.entity b/examples/hass-intent-dataset/base_locale/sl/domain.entity new file mode 100644 index 00000000..e062292e --- /dev/null +++ b/examples/hass-intent-dataset/base_locale/sl/domain.entity @@ -0,0 +1,19 @@ +light +fan +switch +cover +climate +media_player +sensor +binary_sensor +lock +vacuum +timer +input_boolean +scene +script +automation +weather +camera +humidifier +water_heater diff --git a/examples/hass-intent-dataset/base_locale/sl/floor.entity b/examples/hass-intent-dataset/base_locale/sl/floor.entity new file mode 100644 index 00000000..41f8ddbc --- /dev/null +++ b/examples/hass-intent-dataset/base_locale/sl/floor.entity @@ -0,0 +1,5 @@ +ground floor +first floor +second floor +basement +attic diff --git a/examples/hass-intent-dataset/base_locale/sl/name.entity b/examples/hass-intent-dataset/base_locale/sl/name.entity new file mode 100644 index 00000000..b40f02a9 --- /dev/null +++ b/examples/hass-intent-dataset/base_locale/sl/name.entity @@ -0,0 +1,10 @@ +kitchen light +living room light +bedroom light +ceiling fan +thermostat +tv +speaker +front door +garage door +vacuum diff --git a/examples/hass-intent-dataset/base_locale/sl/state.entity b/examples/hass-intent-dataset/base_locale/sl/state.entity new file mode 100644 index 00000000..4faa6b78 --- /dev/null +++ b/examples/hass-intent-dataset/base_locale/sl/state.entity @@ -0,0 +1,6 @@ +on +off +open +closed +locked +unlocked diff --git a/examples/hass-intent-dataset/base_locale/sr-Latn/area.entity b/examples/hass-intent-dataset/base_locale/sr-Latn/area.entity new file mode 100644 index 00000000..76e202de --- /dev/null +++ b/examples/hass-intent-dataset/base_locale/sr-Latn/area.entity @@ -0,0 +1,12 @@ +kuhinja +dnevna soba +spavaća soba +kupatilo +kancelarija +garaža +hodnik +podrum +trpezarija +bašta +terasa +vešernica diff --git a/examples/hass-intent-dataset/base_locale/sr-Latn/color.entity b/examples/hass-intent-dataset/base_locale/sr-Latn/color.entity new file mode 100644 index 00000000..76285fdf --- /dev/null +++ b/examples/hass-intent-dataset/base_locale/sr-Latn/color.entity @@ -0,0 +1,11 @@ +white +black +red +orange +yellow +green +blue +purple +brown +pink +turquoise diff --git a/examples/hass-intent-dataset/base_locale/sr-Latn/device_class.entity b/examples/hass-intent-dataset/base_locale/sr-Latn/device_class.entity new file mode 100644 index 00000000..8878fb15 --- /dev/null +++ b/examples/hass-intent-dataset/base_locale/sr-Latn/device_class.entity @@ -0,0 +1,32 @@ +awning +blind +curtain +door +garage +gate +shade +shutter +window +battery +carbon_monoxide +cold +connectivity +gas +heat +light +lock +moisture +motion +occupancy +opening +plug +power +presence +problem +running +safety +smoke +sound +tamper +update +vibration diff --git a/examples/hass-intent-dataset/base_locale/sr-Latn/domain.entity b/examples/hass-intent-dataset/base_locale/sr-Latn/domain.entity new file mode 100644 index 00000000..e062292e --- /dev/null +++ b/examples/hass-intent-dataset/base_locale/sr-Latn/domain.entity @@ -0,0 +1,19 @@ +light +fan +switch +cover +climate +media_player +sensor +binary_sensor +lock +vacuum +timer +input_boolean +scene +script +automation +weather +camera +humidifier +water_heater diff --git a/examples/hass-intent-dataset/base_locale/sr-Latn/floor.entity b/examples/hass-intent-dataset/base_locale/sr-Latn/floor.entity new file mode 100644 index 00000000..41f8ddbc --- /dev/null +++ b/examples/hass-intent-dataset/base_locale/sr-Latn/floor.entity @@ -0,0 +1,5 @@ +ground floor +first floor +second floor +basement +attic diff --git a/examples/hass-intent-dataset/base_locale/sr-Latn/name.entity b/examples/hass-intent-dataset/base_locale/sr-Latn/name.entity new file mode 100644 index 00000000..b40f02a9 --- /dev/null +++ b/examples/hass-intent-dataset/base_locale/sr-Latn/name.entity @@ -0,0 +1,10 @@ +kitchen light +living room light +bedroom light +ceiling fan +thermostat +tv +speaker +front door +garage door +vacuum diff --git a/examples/hass-intent-dataset/base_locale/sr-Latn/state.entity b/examples/hass-intent-dataset/base_locale/sr-Latn/state.entity new file mode 100644 index 00000000..4faa6b78 --- /dev/null +++ b/examples/hass-intent-dataset/base_locale/sr-Latn/state.entity @@ -0,0 +1,6 @@ +on +off +open +closed +locked +unlocked diff --git a/examples/hass-intent-dataset/base_locale/sr/area.entity b/examples/hass-intent-dataset/base_locale/sr/area.entity new file mode 100644 index 00000000..6bb8f395 --- /dev/null +++ b/examples/hass-intent-dataset/base_locale/sr/area.entity @@ -0,0 +1,12 @@ +кухиња +дневна соба +спаваћа соба +купатило +канцеларија +гаража +ходник +подрум +трпезарија +башта +тераса +вешерница diff --git a/examples/hass-intent-dataset/base_locale/sr/color.entity b/examples/hass-intent-dataset/base_locale/sr/color.entity new file mode 100644 index 00000000..76285fdf --- /dev/null +++ b/examples/hass-intent-dataset/base_locale/sr/color.entity @@ -0,0 +1,11 @@ +white +black +red +orange +yellow +green +blue +purple +brown +pink +turquoise diff --git a/examples/hass-intent-dataset/base_locale/sr/device_class.entity b/examples/hass-intent-dataset/base_locale/sr/device_class.entity new file mode 100644 index 00000000..8878fb15 --- /dev/null +++ b/examples/hass-intent-dataset/base_locale/sr/device_class.entity @@ -0,0 +1,32 @@ +awning +blind +curtain +door +garage +gate +shade +shutter +window +battery +carbon_monoxide +cold +connectivity +gas +heat +light +lock +moisture +motion +occupancy +opening +plug +power +presence +problem +running +safety +smoke +sound +tamper +update +vibration diff --git a/examples/hass-intent-dataset/base_locale/sr/domain.entity b/examples/hass-intent-dataset/base_locale/sr/domain.entity new file mode 100644 index 00000000..e062292e --- /dev/null +++ b/examples/hass-intent-dataset/base_locale/sr/domain.entity @@ -0,0 +1,19 @@ +light +fan +switch +cover +climate +media_player +sensor +binary_sensor +lock +vacuum +timer +input_boolean +scene +script +automation +weather +camera +humidifier +water_heater diff --git a/examples/hass-intent-dataset/base_locale/sr/floor.entity b/examples/hass-intent-dataset/base_locale/sr/floor.entity new file mode 100644 index 00000000..41f8ddbc --- /dev/null +++ b/examples/hass-intent-dataset/base_locale/sr/floor.entity @@ -0,0 +1,5 @@ +ground floor +first floor +second floor +basement +attic diff --git a/examples/hass-intent-dataset/base_locale/sr/name.entity b/examples/hass-intent-dataset/base_locale/sr/name.entity new file mode 100644 index 00000000..b40f02a9 --- /dev/null +++ b/examples/hass-intent-dataset/base_locale/sr/name.entity @@ -0,0 +1,10 @@ +kitchen light +living room light +bedroom light +ceiling fan +thermostat +tv +speaker +front door +garage door +vacuum diff --git a/examples/hass-intent-dataset/base_locale/sr/state.entity b/examples/hass-intent-dataset/base_locale/sr/state.entity new file mode 100644 index 00000000..4faa6b78 --- /dev/null +++ b/examples/hass-intent-dataset/base_locale/sr/state.entity @@ -0,0 +1,6 @@ +on +off +open +closed +locked +unlocked diff --git a/examples/hass-intent-dataset/base_locale/sv/area.entity b/examples/hass-intent-dataset/base_locale/sv/area.entity new file mode 100644 index 00000000..3b043d1e --- /dev/null +++ b/examples/hass-intent-dataset/base_locale/sv/area.entity @@ -0,0 +1,12 @@ +kök +vardagsrum +sovrum +badrum +kontor +garage +hall +källare +matsal +trädgård +terrass +tvättstuga diff --git a/examples/hass-intent-dataset/base_locale/sv/color.entity b/examples/hass-intent-dataset/base_locale/sv/color.entity new file mode 100644 index 00000000..6cebc7e3 --- /dev/null +++ b/examples/hass-intent-dataset/base_locale/sv/color.entity @@ -0,0 +1,11 @@ +vit +svart +röd +orange +gul +grön +blå +lila +brun +rosa +turkos diff --git a/examples/hass-intent-dataset/base_locale/sv/device_class.entity b/examples/hass-intent-dataset/base_locale/sv/device_class.entity new file mode 100644 index 00000000..8878fb15 --- /dev/null +++ b/examples/hass-intent-dataset/base_locale/sv/device_class.entity @@ -0,0 +1,32 @@ +awning +blind +curtain +door +garage +gate +shade +shutter +window +battery +carbon_monoxide +cold +connectivity +gas +heat +light +lock +moisture +motion +occupancy +opening +plug +power +presence +problem +running +safety +smoke +sound +tamper +update +vibration diff --git a/examples/hass-intent-dataset/base_locale/sv/domain.entity b/examples/hass-intent-dataset/base_locale/sv/domain.entity new file mode 100644 index 00000000..e062292e --- /dev/null +++ b/examples/hass-intent-dataset/base_locale/sv/domain.entity @@ -0,0 +1,19 @@ +light +fan +switch +cover +climate +media_player +sensor +binary_sensor +lock +vacuum +timer +input_boolean +scene +script +automation +weather +camera +humidifier +water_heater diff --git a/examples/hass-intent-dataset/base_locale/sv/floor.entity b/examples/hass-intent-dataset/base_locale/sv/floor.entity new file mode 100644 index 00000000..41f8ddbc --- /dev/null +++ b/examples/hass-intent-dataset/base_locale/sv/floor.entity @@ -0,0 +1,5 @@ +ground floor +first floor +second floor +basement +attic diff --git a/examples/hass-intent-dataset/base_locale/sv/name.entity b/examples/hass-intent-dataset/base_locale/sv/name.entity new file mode 100644 index 00000000..b40f02a9 --- /dev/null +++ b/examples/hass-intent-dataset/base_locale/sv/name.entity @@ -0,0 +1,10 @@ +kitchen light +living room light +bedroom light +ceiling fan +thermostat +tv +speaker +front door +garage door +vacuum diff --git a/examples/hass-intent-dataset/base_locale/sv/state.entity b/examples/hass-intent-dataset/base_locale/sv/state.entity new file mode 100644 index 00000000..78c2fcf2 --- /dev/null +++ b/examples/hass-intent-dataset/base_locale/sv/state.entity @@ -0,0 +1,6 @@ +på +av +öppen +stängd +låst +olåst diff --git a/examples/hass-intent-dataset/base_locale/sw/area.entity b/examples/hass-intent-dataset/base_locale/sw/area.entity new file mode 100644 index 00000000..ffac3a0c --- /dev/null +++ b/examples/hass-intent-dataset/base_locale/sw/area.entity @@ -0,0 +1,12 @@ +jikoni +sebule +chumba cha kulala +bafuni +ofisi +gereji +barabara ya ukumbi +ghorofa ya chini +chumba cha kulia +bustani +mtaro +chumba cha kufulia diff --git a/examples/hass-intent-dataset/base_locale/sw/color.entity b/examples/hass-intent-dataset/base_locale/sw/color.entity new file mode 100644 index 00000000..76285fdf --- /dev/null +++ b/examples/hass-intent-dataset/base_locale/sw/color.entity @@ -0,0 +1,11 @@ +white +black +red +orange +yellow +green +blue +purple +brown +pink +turquoise diff --git a/examples/hass-intent-dataset/base_locale/sw/device_class.entity b/examples/hass-intent-dataset/base_locale/sw/device_class.entity new file mode 100644 index 00000000..8878fb15 --- /dev/null +++ b/examples/hass-intent-dataset/base_locale/sw/device_class.entity @@ -0,0 +1,32 @@ +awning +blind +curtain +door +garage +gate +shade +shutter +window +battery +carbon_monoxide +cold +connectivity +gas +heat +light +lock +moisture +motion +occupancy +opening +plug +power +presence +problem +running +safety +smoke +sound +tamper +update +vibration diff --git a/examples/hass-intent-dataset/base_locale/sw/domain.entity b/examples/hass-intent-dataset/base_locale/sw/domain.entity new file mode 100644 index 00000000..e062292e --- /dev/null +++ b/examples/hass-intent-dataset/base_locale/sw/domain.entity @@ -0,0 +1,19 @@ +light +fan +switch +cover +climate +media_player +sensor +binary_sensor +lock +vacuum +timer +input_boolean +scene +script +automation +weather +camera +humidifier +water_heater diff --git a/examples/hass-intent-dataset/base_locale/sw/floor.entity b/examples/hass-intent-dataset/base_locale/sw/floor.entity new file mode 100644 index 00000000..41f8ddbc --- /dev/null +++ b/examples/hass-intent-dataset/base_locale/sw/floor.entity @@ -0,0 +1,5 @@ +ground floor +first floor +second floor +basement +attic diff --git a/examples/hass-intent-dataset/base_locale/sw/name.entity b/examples/hass-intent-dataset/base_locale/sw/name.entity new file mode 100644 index 00000000..b40f02a9 --- /dev/null +++ b/examples/hass-intent-dataset/base_locale/sw/name.entity @@ -0,0 +1,10 @@ +kitchen light +living room light +bedroom light +ceiling fan +thermostat +tv +speaker +front door +garage door +vacuum diff --git a/examples/hass-intent-dataset/base_locale/sw/state.entity b/examples/hass-intent-dataset/base_locale/sw/state.entity new file mode 100644 index 00000000..4faa6b78 --- /dev/null +++ b/examples/hass-intent-dataset/base_locale/sw/state.entity @@ -0,0 +1,6 @@ +on +off +open +closed +locked +unlocked diff --git a/examples/hass-intent-dataset/base_locale/ta/area.entity b/examples/hass-intent-dataset/base_locale/ta/area.entity new file mode 100644 index 00000000..e317ac75 --- /dev/null +++ b/examples/hass-intent-dataset/base_locale/ta/area.entity @@ -0,0 +1,12 @@ +சமையலறை +பெற்று அறை +கடுக்கை அறை +குளியலறை +பணி இடம் +வாகன நிறுத்தம் +நடைபாதை +அடித்தளம் +உணவு அறை +தோட்டம் +முற்றம் +துவைப்பு அறை diff --git a/examples/hass-intent-dataset/base_locale/ta/color.entity b/examples/hass-intent-dataset/base_locale/ta/color.entity new file mode 100644 index 00000000..76285fdf --- /dev/null +++ b/examples/hass-intent-dataset/base_locale/ta/color.entity @@ -0,0 +1,11 @@ +white +black +red +orange +yellow +green +blue +purple +brown +pink +turquoise diff --git a/examples/hass-intent-dataset/base_locale/ta/device_class.entity b/examples/hass-intent-dataset/base_locale/ta/device_class.entity new file mode 100644 index 00000000..8878fb15 --- /dev/null +++ b/examples/hass-intent-dataset/base_locale/ta/device_class.entity @@ -0,0 +1,32 @@ +awning +blind +curtain +door +garage +gate +shade +shutter +window +battery +carbon_monoxide +cold +connectivity +gas +heat +light +lock +moisture +motion +occupancy +opening +plug +power +presence +problem +running +safety +smoke +sound +tamper +update +vibration diff --git a/examples/hass-intent-dataset/base_locale/ta/domain.entity b/examples/hass-intent-dataset/base_locale/ta/domain.entity new file mode 100644 index 00000000..e062292e --- /dev/null +++ b/examples/hass-intent-dataset/base_locale/ta/domain.entity @@ -0,0 +1,19 @@ +light +fan +switch +cover +climate +media_player +sensor +binary_sensor +lock +vacuum +timer +input_boolean +scene +script +automation +weather +camera +humidifier +water_heater diff --git a/examples/hass-intent-dataset/base_locale/ta/floor.entity b/examples/hass-intent-dataset/base_locale/ta/floor.entity new file mode 100644 index 00000000..41f8ddbc --- /dev/null +++ b/examples/hass-intent-dataset/base_locale/ta/floor.entity @@ -0,0 +1,5 @@ +ground floor +first floor +second floor +basement +attic diff --git a/examples/hass-intent-dataset/base_locale/ta/name.entity b/examples/hass-intent-dataset/base_locale/ta/name.entity new file mode 100644 index 00000000..b40f02a9 --- /dev/null +++ b/examples/hass-intent-dataset/base_locale/ta/name.entity @@ -0,0 +1,10 @@ +kitchen light +living room light +bedroom light +ceiling fan +thermostat +tv +speaker +front door +garage door +vacuum diff --git a/examples/hass-intent-dataset/base_locale/ta/state.entity b/examples/hass-intent-dataset/base_locale/ta/state.entity new file mode 100644 index 00000000..4faa6b78 --- /dev/null +++ b/examples/hass-intent-dataset/base_locale/ta/state.entity @@ -0,0 +1,6 @@ +on +off +open +closed +locked +unlocked diff --git a/examples/hass-intent-dataset/base_locale/te/area.entity b/examples/hass-intent-dataset/base_locale/te/area.entity new file mode 100644 index 00000000..ddb54fdc --- /dev/null +++ b/examples/hass-intent-dataset/base_locale/te/area.entity @@ -0,0 +1,12 @@ +వంటగది +సభా గది +నిద్ర గది +స్నానగది +కార్యాలయం +గ్యారేజ్ +కారిడార్ +బేస్మెంట్ +భోజన గది +తోట +టెరస్ +ఉతికే గది diff --git a/examples/hass-intent-dataset/base_locale/te/color.entity b/examples/hass-intent-dataset/base_locale/te/color.entity new file mode 100644 index 00000000..76285fdf --- /dev/null +++ b/examples/hass-intent-dataset/base_locale/te/color.entity @@ -0,0 +1,11 @@ +white +black +red +orange +yellow +green +blue +purple +brown +pink +turquoise diff --git a/examples/hass-intent-dataset/base_locale/te/device_class.entity b/examples/hass-intent-dataset/base_locale/te/device_class.entity new file mode 100644 index 00000000..8878fb15 --- /dev/null +++ b/examples/hass-intent-dataset/base_locale/te/device_class.entity @@ -0,0 +1,32 @@ +awning +blind +curtain +door +garage +gate +shade +shutter +window +battery +carbon_monoxide +cold +connectivity +gas +heat +light +lock +moisture +motion +occupancy +opening +plug +power +presence +problem +running +safety +smoke +sound +tamper +update +vibration diff --git a/examples/hass-intent-dataset/base_locale/te/domain.entity b/examples/hass-intent-dataset/base_locale/te/domain.entity new file mode 100644 index 00000000..e062292e --- /dev/null +++ b/examples/hass-intent-dataset/base_locale/te/domain.entity @@ -0,0 +1,19 @@ +light +fan +switch +cover +climate +media_player +sensor +binary_sensor +lock +vacuum +timer +input_boolean +scene +script +automation +weather +camera +humidifier +water_heater diff --git a/examples/hass-intent-dataset/base_locale/te/floor.entity b/examples/hass-intent-dataset/base_locale/te/floor.entity new file mode 100644 index 00000000..41f8ddbc --- /dev/null +++ b/examples/hass-intent-dataset/base_locale/te/floor.entity @@ -0,0 +1,5 @@ +ground floor +first floor +second floor +basement +attic diff --git a/examples/hass-intent-dataset/base_locale/te/name.entity b/examples/hass-intent-dataset/base_locale/te/name.entity new file mode 100644 index 00000000..b40f02a9 --- /dev/null +++ b/examples/hass-intent-dataset/base_locale/te/name.entity @@ -0,0 +1,10 @@ +kitchen light +living room light +bedroom light +ceiling fan +thermostat +tv +speaker +front door +garage door +vacuum diff --git a/examples/hass-intent-dataset/base_locale/te/state.entity b/examples/hass-intent-dataset/base_locale/te/state.entity new file mode 100644 index 00000000..4faa6b78 --- /dev/null +++ b/examples/hass-intent-dataset/base_locale/te/state.entity @@ -0,0 +1,6 @@ +on +off +open +closed +locked +unlocked diff --git a/examples/hass-intent-dataset/base_locale/uk/area.entity b/examples/hass-intent-dataset/base_locale/uk/area.entity new file mode 100644 index 00000000..6f401c72 --- /dev/null +++ b/examples/hass-intent-dataset/base_locale/uk/area.entity @@ -0,0 +1,12 @@ +кухня +вітальня +спальня +ванна +офіс +гараж +коридор +підвал +їдальня +сад +тераса +пральня diff --git a/examples/hass-intent-dataset/base_locale/uk/color.entity b/examples/hass-intent-dataset/base_locale/uk/color.entity new file mode 100644 index 00000000..76285fdf --- /dev/null +++ b/examples/hass-intent-dataset/base_locale/uk/color.entity @@ -0,0 +1,11 @@ +white +black +red +orange +yellow +green +blue +purple +brown +pink +turquoise diff --git a/examples/hass-intent-dataset/base_locale/uk/device_class.entity b/examples/hass-intent-dataset/base_locale/uk/device_class.entity new file mode 100644 index 00000000..8878fb15 --- /dev/null +++ b/examples/hass-intent-dataset/base_locale/uk/device_class.entity @@ -0,0 +1,32 @@ +awning +blind +curtain +door +garage +gate +shade +shutter +window +battery +carbon_monoxide +cold +connectivity +gas +heat +light +lock +moisture +motion +occupancy +opening +plug +power +presence +problem +running +safety +smoke +sound +tamper +update +vibration diff --git a/examples/hass-intent-dataset/base_locale/uk/domain.entity b/examples/hass-intent-dataset/base_locale/uk/domain.entity new file mode 100644 index 00000000..e062292e --- /dev/null +++ b/examples/hass-intent-dataset/base_locale/uk/domain.entity @@ -0,0 +1,19 @@ +light +fan +switch +cover +climate +media_player +sensor +binary_sensor +lock +vacuum +timer +input_boolean +scene +script +automation +weather +camera +humidifier +water_heater diff --git a/examples/hass-intent-dataset/base_locale/uk/floor.entity b/examples/hass-intent-dataset/base_locale/uk/floor.entity new file mode 100644 index 00000000..41f8ddbc --- /dev/null +++ b/examples/hass-intent-dataset/base_locale/uk/floor.entity @@ -0,0 +1,5 @@ +ground floor +first floor +second floor +basement +attic diff --git a/examples/hass-intent-dataset/base_locale/uk/name.entity b/examples/hass-intent-dataset/base_locale/uk/name.entity new file mode 100644 index 00000000..b40f02a9 --- /dev/null +++ b/examples/hass-intent-dataset/base_locale/uk/name.entity @@ -0,0 +1,10 @@ +kitchen light +living room light +bedroom light +ceiling fan +thermostat +tv +speaker +front door +garage door +vacuum diff --git a/examples/hass-intent-dataset/base_locale/uk/state.entity b/examples/hass-intent-dataset/base_locale/uk/state.entity new file mode 100644 index 00000000..4faa6b78 --- /dev/null +++ b/examples/hass-intent-dataset/base_locale/uk/state.entity @@ -0,0 +1,6 @@ +on +off +open +closed +locked +unlocked diff --git a/examples/hass-intent-dataset/base_locale/ur/area.entity b/examples/hass-intent-dataset/base_locale/ur/area.entity new file mode 100644 index 00000000..35e84843 --- /dev/null +++ b/examples/hass-intent-dataset/base_locale/ur/area.entity @@ -0,0 +1,12 @@ +باتھ روم +بیٹھک +سونے کا کمرہ +باتھ روم +دفتر +گیراج +گلی +تہہ خانہ +کھانے کا کمرہ +باغ +چبوترا +دھونے کا کمرہ diff --git a/examples/hass-intent-dataset/base_locale/ur/color.entity b/examples/hass-intent-dataset/base_locale/ur/color.entity new file mode 100644 index 00000000..76285fdf --- /dev/null +++ b/examples/hass-intent-dataset/base_locale/ur/color.entity @@ -0,0 +1,11 @@ +white +black +red +orange +yellow +green +blue +purple +brown +pink +turquoise diff --git a/examples/hass-intent-dataset/base_locale/ur/device_class.entity b/examples/hass-intent-dataset/base_locale/ur/device_class.entity new file mode 100644 index 00000000..8878fb15 --- /dev/null +++ b/examples/hass-intent-dataset/base_locale/ur/device_class.entity @@ -0,0 +1,32 @@ +awning +blind +curtain +door +garage +gate +shade +shutter +window +battery +carbon_monoxide +cold +connectivity +gas +heat +light +lock +moisture +motion +occupancy +opening +plug +power +presence +problem +running +safety +smoke +sound +tamper +update +vibration diff --git a/examples/hass-intent-dataset/base_locale/ur/domain.entity b/examples/hass-intent-dataset/base_locale/ur/domain.entity new file mode 100644 index 00000000..e062292e --- /dev/null +++ b/examples/hass-intent-dataset/base_locale/ur/domain.entity @@ -0,0 +1,19 @@ +light +fan +switch +cover +climate +media_player +sensor +binary_sensor +lock +vacuum +timer +input_boolean +scene +script +automation +weather +camera +humidifier +water_heater diff --git a/examples/hass-intent-dataset/base_locale/ur/floor.entity b/examples/hass-intent-dataset/base_locale/ur/floor.entity new file mode 100644 index 00000000..41f8ddbc --- /dev/null +++ b/examples/hass-intent-dataset/base_locale/ur/floor.entity @@ -0,0 +1,5 @@ +ground floor +first floor +second floor +basement +attic diff --git a/examples/hass-intent-dataset/base_locale/ur/name.entity b/examples/hass-intent-dataset/base_locale/ur/name.entity new file mode 100644 index 00000000..b40f02a9 --- /dev/null +++ b/examples/hass-intent-dataset/base_locale/ur/name.entity @@ -0,0 +1,10 @@ +kitchen light +living room light +bedroom light +ceiling fan +thermostat +tv +speaker +front door +garage door +vacuum diff --git a/examples/hass-intent-dataset/base_locale/ur/state.entity b/examples/hass-intent-dataset/base_locale/ur/state.entity new file mode 100644 index 00000000..4faa6b78 --- /dev/null +++ b/examples/hass-intent-dataset/base_locale/ur/state.entity @@ -0,0 +1,6 @@ +on +off +open +closed +locked +unlocked diff --git a/examples/hass-intent-dataset/base_locale/zh-CN/area.entity b/examples/hass-intent-dataset/base_locale/zh-CN/area.entity new file mode 100644 index 00000000..75df5a93 --- /dev/null +++ b/examples/hass-intent-dataset/base_locale/zh-CN/area.entity @@ -0,0 +1,12 @@ +厨房 +客厅 +卧室 +浴室 +办公室 +车库 +走廊 +地下室 +餐厅 +花园 +露台 +洗衣房 diff --git a/examples/hass-intent-dataset/base_locale/zh-CN/color.entity b/examples/hass-intent-dataset/base_locale/zh-CN/color.entity new file mode 100644 index 00000000..48dcd927 --- /dev/null +++ b/examples/hass-intent-dataset/base_locale/zh-CN/color.entity @@ -0,0 +1,11 @@ +白色 +黑色 +红色 +橙色 +黄色 +绿色 +蓝色 +紫色 +棕色 +粉色 +青色 diff --git a/examples/hass-intent-dataset/base_locale/zh-CN/device_class.entity b/examples/hass-intent-dataset/base_locale/zh-CN/device_class.entity new file mode 100644 index 00000000..8878fb15 --- /dev/null +++ b/examples/hass-intent-dataset/base_locale/zh-CN/device_class.entity @@ -0,0 +1,32 @@ +awning +blind +curtain +door +garage +gate +shade +shutter +window +battery +carbon_monoxide +cold +connectivity +gas +heat +light +lock +moisture +motion +occupancy +opening +plug +power +presence +problem +running +safety +smoke +sound +tamper +update +vibration diff --git a/examples/hass-intent-dataset/base_locale/zh-CN/domain.entity b/examples/hass-intent-dataset/base_locale/zh-CN/domain.entity new file mode 100644 index 00000000..e062292e --- /dev/null +++ b/examples/hass-intent-dataset/base_locale/zh-CN/domain.entity @@ -0,0 +1,19 @@ +light +fan +switch +cover +climate +media_player +sensor +binary_sensor +lock +vacuum +timer +input_boolean +scene +script +automation +weather +camera +humidifier +water_heater diff --git a/examples/hass-intent-dataset/base_locale/zh-CN/floor.entity b/examples/hass-intent-dataset/base_locale/zh-CN/floor.entity new file mode 100644 index 00000000..29943e7a --- /dev/null +++ b/examples/hass-intent-dataset/base_locale/zh-CN/floor.entity @@ -0,0 +1,5 @@ +一楼 +二楼 +三楼 +地下室 +阁楼 diff --git a/examples/hass-intent-dataset/base_locale/zh-CN/name.entity b/examples/hass-intent-dataset/base_locale/zh-CN/name.entity new file mode 100644 index 00000000..d2adeeb3 --- /dev/null +++ b/examples/hass-intent-dataset/base_locale/zh-CN/name.entity @@ -0,0 +1,10 @@ +厨房灯 +客厅灯 +卧室灯 +吊扇 +恒温器 +电视 +音响 +前门 +车库门 +吸尘器 diff --git a/examples/hass-intent-dataset/base_locale/zh-CN/state.entity b/examples/hass-intent-dataset/base_locale/zh-CN/state.entity new file mode 100644 index 00000000..a98bce40 --- /dev/null +++ b/examples/hass-intent-dataset/base_locale/zh-CN/state.entity @@ -0,0 +1,6 @@ +开 +关 +打开 +关闭 +锁定 +解锁 diff --git a/examples/hass-intent-dataset/base_locale/zh-HK/area.entity b/examples/hass-intent-dataset/base_locale/zh-HK/area.entity new file mode 100644 index 00000000..a542ba13 --- /dev/null +++ b/examples/hass-intent-dataset/base_locale/zh-HK/area.entity @@ -0,0 +1,12 @@ +廚房 +客廳 +臥室 +浴室 +辦公室 +車庫 +走廊 +地下室 +餐廳 +花園 +露台 +洗衣房 diff --git a/examples/hass-intent-dataset/base_locale/zh-HK/color.entity b/examples/hass-intent-dataset/base_locale/zh-HK/color.entity new file mode 100644 index 00000000..76285fdf --- /dev/null +++ b/examples/hass-intent-dataset/base_locale/zh-HK/color.entity @@ -0,0 +1,11 @@ +white +black +red +orange +yellow +green +blue +purple +brown +pink +turquoise diff --git a/examples/hass-intent-dataset/base_locale/zh-HK/device_class.entity b/examples/hass-intent-dataset/base_locale/zh-HK/device_class.entity new file mode 100644 index 00000000..8878fb15 --- /dev/null +++ b/examples/hass-intent-dataset/base_locale/zh-HK/device_class.entity @@ -0,0 +1,32 @@ +awning +blind +curtain +door +garage +gate +shade +shutter +window +battery +carbon_monoxide +cold +connectivity +gas +heat +light +lock +moisture +motion +occupancy +opening +plug +power +presence +problem +running +safety +smoke +sound +tamper +update +vibration diff --git a/examples/hass-intent-dataset/base_locale/zh-HK/domain.entity b/examples/hass-intent-dataset/base_locale/zh-HK/domain.entity new file mode 100644 index 00000000..e062292e --- /dev/null +++ b/examples/hass-intent-dataset/base_locale/zh-HK/domain.entity @@ -0,0 +1,19 @@ +light +fan +switch +cover +climate +media_player +sensor +binary_sensor +lock +vacuum +timer +input_boolean +scene +script +automation +weather +camera +humidifier +water_heater diff --git a/examples/hass-intent-dataset/base_locale/zh-HK/floor.entity b/examples/hass-intent-dataset/base_locale/zh-HK/floor.entity new file mode 100644 index 00000000..41f8ddbc --- /dev/null +++ b/examples/hass-intent-dataset/base_locale/zh-HK/floor.entity @@ -0,0 +1,5 @@ +ground floor +first floor +second floor +basement +attic diff --git a/examples/hass-intent-dataset/base_locale/zh-HK/name.entity b/examples/hass-intent-dataset/base_locale/zh-HK/name.entity new file mode 100644 index 00000000..f6e581c9 --- /dev/null +++ b/examples/hass-intent-dataset/base_locale/zh-HK/name.entity @@ -0,0 +1,10 @@ +廚房燈 +客廳燈 +睡房燈 +吊扇 +恆溫器 +電視 +音響 +大門 +車房門 +吸塵機 diff --git a/examples/hass-intent-dataset/base_locale/zh-HK/state.entity b/examples/hass-intent-dataset/base_locale/zh-HK/state.entity new file mode 100644 index 00000000..4faa6b78 --- /dev/null +++ b/examples/hass-intent-dataset/base_locale/zh-HK/state.entity @@ -0,0 +1,6 @@ +on +off +open +closed +locked +unlocked diff --git a/examples/hass-intent-dataset/base_locale/zh-TW/area.entity b/examples/hass-intent-dataset/base_locale/zh-TW/area.entity new file mode 100644 index 00000000..a542ba13 --- /dev/null +++ b/examples/hass-intent-dataset/base_locale/zh-TW/area.entity @@ -0,0 +1,12 @@ +廚房 +客廳 +臥室 +浴室 +辦公室 +車庫 +走廊 +地下室 +餐廳 +花園 +露台 +洗衣房 diff --git a/examples/hass-intent-dataset/base_locale/zh-TW/color.entity b/examples/hass-intent-dataset/base_locale/zh-TW/color.entity new file mode 100644 index 00000000..76285fdf --- /dev/null +++ b/examples/hass-intent-dataset/base_locale/zh-TW/color.entity @@ -0,0 +1,11 @@ +white +black +red +orange +yellow +green +blue +purple +brown +pink +turquoise diff --git a/examples/hass-intent-dataset/base_locale/zh-TW/device_class.entity b/examples/hass-intent-dataset/base_locale/zh-TW/device_class.entity new file mode 100644 index 00000000..8878fb15 --- /dev/null +++ b/examples/hass-intent-dataset/base_locale/zh-TW/device_class.entity @@ -0,0 +1,32 @@ +awning +blind +curtain +door +garage +gate +shade +shutter +window +battery +carbon_monoxide +cold +connectivity +gas +heat +light +lock +moisture +motion +occupancy +opening +plug +power +presence +problem +running +safety +smoke +sound +tamper +update +vibration diff --git a/examples/hass-intent-dataset/base_locale/zh-TW/domain.entity b/examples/hass-intent-dataset/base_locale/zh-TW/domain.entity new file mode 100644 index 00000000..e062292e --- /dev/null +++ b/examples/hass-intent-dataset/base_locale/zh-TW/domain.entity @@ -0,0 +1,19 @@ +light +fan +switch +cover +climate +media_player +sensor +binary_sensor +lock +vacuum +timer +input_boolean +scene +script +automation +weather +camera +humidifier +water_heater diff --git a/examples/hass-intent-dataset/base_locale/zh-TW/floor.entity b/examples/hass-intent-dataset/base_locale/zh-TW/floor.entity new file mode 100644 index 00000000..41f8ddbc --- /dev/null +++ b/examples/hass-intent-dataset/base_locale/zh-TW/floor.entity @@ -0,0 +1,5 @@ +ground floor +first floor +second floor +basement +attic diff --git a/examples/hass-intent-dataset/base_locale/zh-TW/name.entity b/examples/hass-intent-dataset/base_locale/zh-TW/name.entity new file mode 100644 index 00000000..5c5cfec3 --- /dev/null +++ b/examples/hass-intent-dataset/base_locale/zh-TW/name.entity @@ -0,0 +1,10 @@ +廚房燈 +客廳燈 +臥室燈 +吊扇 +恆溫器 +電視 +音響 +前門 +車庫門 +吸塵器 diff --git a/examples/hass-intent-dataset/base_locale/zh-TW/state.entity b/examples/hass-intent-dataset/base_locale/zh-TW/state.entity new file mode 100644 index 00000000..4faa6b78 --- /dev/null +++ b/examples/hass-intent-dataset/base_locale/zh-TW/state.entity @@ -0,0 +1,6 @@ +on +off +open +closed +locked +unlocked diff --git a/examples/hass-intent-dataset/convert_hassil_intents.py b/examples/hass-intent-dataset/convert_hassil_intents.py index ff644c57..3089d16d 100644 --- a/examples/hass-intent-dataset/convert_hassil_intents.py +++ b/examples/hass-intent-dataset/convert_hassil_intents.py @@ -709,72 +709,20 @@ # best-effort list; contributors can extend it for missing languages. # --------------------------------------------------------------------------- -COMMON_AREA_NAMES: dict[str, list[str]] = { - "en": ["kitchen", "living room", "bedroom", "bathroom", "office", "garage", "hallway", "basement", "dining room", "garden", "terrace", "laundry room"], - "de": ["Küche", "Wohnzimmer", "Schlafzimmer", "Badezimmer", "Büro", "Garage", "Flur", "Keller", "Esszimmer", "Garten", "Terrasse", "Waschküche"], - "fr": ["cuisine", "salon", "chambre", "salle de bain", "bureau", "garage", "couloir", "sous-sol", "salle à manger", "jardin", "terrasse", "buanderie"], - "es": ["cocina", "salón", "dormitorio", "baño", "oficina", "garaje", "pasillo", "sótano", "comedor", "jardín", "terraza", "lavandería"], - "pt": ["cozinha", "sala de estar", "quarto", "casa de banho", "escritório", "garagem", "corredor", "cave", "sala de jantar", "jardim", "terraço", "lavandaria"], - "pt-BR": ["cozinha", "sala", "quarto", "banheiro", "escritório", "garagem", "corredor", "porão", "sala de jantar", "jardim", "terraço", "lavanderia"], - "it": ["cucina", "soggiorno", "camera da letto", "bagno", "ufficio", "garage", "corridoio", "seminterrato", "sala da pranzo", "giardino", "terrazza", "lavanderia"], - "nl": ["keuken", "woonkamer", "slaapkamer", "badkamer", "kantoor", "garage", "gang", "kelder", "eetkamer", "tuin", "terras", "wasruimte"], - "ca": ["cuina", "sala d'estar", "dormitori", "bany", "oficina", "garatge", "passadís", "soterrani", "sala de dinar", "jardí", "terrassa", "sala de bugada"], - "da": ["køkken", "stue", "soveværelse", "badeværelse", "kontor", "garage", "gang", "kælder", "spisestue", "have", "terrasse", "vaskerum"], - "sv": ["kök", "vardagsrum", "sovrum", "badrum", "kontor", "garage", "hall", "källare", "matsal", "trädgård", "terrass", "tvättstuga"], - "nb": ["kjøkken", "stue", "soverom", "bad", "kontor", "garasje", "gang", "kjeller", "spisestue", "hage", "terrasse", "vaskerom"], - "fi": ["keittiö", "olohuone", "makuuhuone", "kylpyhuone", "toimisto", "autotalli", "käytävä", "kellari", "ruokailuhuone", "puutarha", "terassi", "pesula"], - "pl": ["kuchnia", "salon", "sypialnia", "łazienka", "biuro", "garaż", "korytarz", "piwnica", "jadalnia", "ogród", "taras", "pralnia"], - "ru": ["кухня", "гостиная", "спальня", "ванная", "офис", "гараж", "коридор", "подвал", "столовая", "сад", "терраса", "прачечная"], - "ro": ["bucătărie", "sufragerie", "dormitor", "baie", "birou", "garaj", "hol", "subsol", "sală de mese", "grădină", "terasă", "spălătorie"], - "ar": ["مطبخ", "غرفة المعيشة", "غرفة النوم", "حمام", "مكتب", "مرآب", "ممر", "قبو", "غرفة الطعام", "حديقة", "شرفة", "غرفة الغسيل"], - "he": ["מטבח", "סלון", "חדר שינה", "אמבטיה", "משרד", "מוסך", "מסדרון", "מרתף", "חדר אוכל", "גן", "מרפסת", "חדר כביסה"], - "ja": ["キッチン", "リビング", "寝室", "浴室", "書斎", "ガレージ", "廊下", "地下室", "ダイニング", "庭", "テラス", "洗濯室"], - "ko": ["부엌", "거실", "침실", "욕실", "사무실", "차고", "복도", "지하실", "식당", "정원", "테라스", "세탁실"], - "zh-CN": ["厨房", "客厅", "卧室", "浴室", "办公室", "车库", "走廊", "地下室", "餐厅", "花园", "露台", "洗衣房"], - "zh-TW": ["廚房", "客廳", "臥室", "浴室", "辦公室", "車庫", "走廊", "地下室", "餐廳", "花園", "露台", "洗衣房"], - "zh-HK": ["廚房", "客廳", "臥室", "浴室", "辦公室", "車庫", "走廊", "地下室", "餐廳", "花園", "露台", "洗衣房"], - "el": ["κουζίνα", "σαλόνι", "υπνοδωμάτιο", "μπάνιο", "γραφείο", "γκαράζ", "διάδρομος", "υπόγειο", "τραπεζαρία", "κήπος", "βεράντα", "πλυντήριο"], - "hu": ["konyha", "nappali", "hálószoba", "fürdőszoba", "iroda", "garázs", "folyosó", "pince", "étkező", "kert", "terasz", "mosókonyha"], - "cs": ["kuchyně", "obývací pokoj", "ložnice", "koupelna", "kancelář", "garáž", "chodba", "sklep", "jídelna", "zahrada", "terasa", "prádelna"], - "sk": ["kuchyňa", "obývačka", "spálňa", "kúpeľňa", "kancelária", "garáž", "chodba", "pivnica", "jedáleň", "záhrada", "terasa", "práčovňa"], - "sl": ["kuhinja", "dnevna soba", "spalnica", "kopalnica", "pisarna", "garaža", "hodnik", "klet", "jedilnica", "vrt", "terasa", "pralnica"], - "hr": ["kuhinja", "dnevni boravak", "spavaća soba", "kupaonica", "ured", "garaža", "hodnik", "podrum", "blagovaonica", "vrt", "terasa", "praona"], - "sr": ["кухиња", "дневна соба", "спаваћа соба", "купатило", "канцеларија", "гаража", "ходник", "подрум", "трпезарија", "башта", "тераса", "пераоница"], - "sr-Latn": ["kuhinja", "dnevna soba", "spavaća soba", "kupatilo", "kancelarija", "garaža", "hodnik", "podrum", "trpezarija", "bašta", "terasa", "peraonica"], - "bg": ["кухня", "хол", "спалня", "баня", "офис", "гараж", "коридор", "мазе", "трапезария", "градина", "тераса", "пералня"], - "uk": ["кухня", "вітальня", "спальня", "ванна кімната", "офіс", "гараж", "коридор", "підвал", "їдальня", "сад", "тераса", "пральня"], - "tr": ["mutfak", "oturma odası", "yatak odası", "banyo", "ofis", "garaj", "koridor", "bodrum", "yemek odası", "bahçe", "teras", "çamaşır odası"], - "th": ["ครัว", "ห้องนั่งเล่น", "ห้องนอน", "ห้องน้ำ", "ห้องทำงาน", "โรงรถ", "ทางเดิน", "ใต้ดิน", "ห้องอาหาร", "สวน", "ระเบียง", "ห้องซักรีด"], - "vi": ["nhà bếp", "phòng khách", "phòng ngủ", "phòng tắm", "văn phòng", "ga-ra", "hành lang", "tầng hầm", "phòng ăn", "vườn", "hiên", "phòng giặt"], - "id": ["dapur", "ruang tamu", "kamar tidur", "kamar mandi", "kantor", "garasi", "koridor", "ruang bawah tanah", "ruang makan", "taman", "teras", "ruang cuci"], - "ms": ["dapur", "ruang tamu", "bilik tidur", "bilik air", "pejabat", "garaj", "koridor", "ruang bawah tanah", "ruang makan", "taman", "teres", "bilik dobi"], - "ga": ["cistin", "seomra suí", "seomra codlata", "seomra folctha", "oifig", "garáiste", "halla", "bunús", "seomra bia", "gairdín", "terrás", "seomra níocháin"], - "cy": ["gegin", "ystafell fyw", "ystafell wely", "ystafell ymolchi", "swyddfa", "garej", "coridor", "celar", "ystafell fwyta", "gardd", "teras", "ystafell golchi"], - "et": ["köök", "elutuba", "magamistuba", "vannituba", "kontor", "garaaž", "koridor", "kelder", "söögituba", "aed", "terass", "pesuruum"], - "lt": ["virtuvė", "svetainė", "miegamasis", "vonios kambarys", "biuras", "garažas", "koridorius", "rūsys", "valgomasis", "sodas", "terasa", "skalbykla"], - "lv": ["virtuve", "dzīvojamā istaba", "guļamistaba", "vannas istaba", "birojs", "garāža", "koridors", "pagrabs", "ēdamistaba", "dārzs", "terase", "veļas mazgātava"], - "is": ["eldhús", "stofa", "svefnherbergi", "baðherbergi", "skrifstofa", "bílskúr", "gangur", "kjallari", "borðstofa", "garður", "svalir", "þvottahús"], - "af": ["kombuis", "sitkamer", "slaapkamer", "badkamer", "kantoor", "motorhuis", "gang", "kelder", "eetkamer", "tuin", "terras", "wasgoedkamer"], - "sw": ["jiko", "sebule", "chumba cha kulala", "bafu", "ofisi", "jengo la magari", "njia", "chumba cha chini", "chumba cha kula", "bustani", "varanda", "chumba cha kufulia"], - "eu": ["sukaldea", "egongela", "logela", "bainugela", "bulegoa", "garajea", "korridorea", "sotoa", "janogela", "lorategia", "terraza", "garbitze-gela"], - "gl": ["cociña", "sala de estar", "dormitorio", "cuarto de baño", "oficina", "garaxe", "corredor", "sótano", "comedor", "xardín", "terraza", "lavandería"], - "fa": ["آشپزخانه", "اتاق نشیمن", "اتاق خواب", "حمام", "دفتر", "گاراژ", "راهرو", "زیرزمین", "اتاق غذاخوری", "باغ", "تراس", "اتاق لباسشویی"], - "ne": ["भान्सा", "बैठक कोठा", "सुत्ने कोठा", "बाथरूम", "कार्यालय", "ग्यारेज", "बार", "भुइँतला", "भोजन कोठा", "बगैंचा", "चर्पी", "धुन पखाल्ने कोठा"], - "ka": ["სამზარეულო", "მისაღები ოთახი", "საძინებელი", "აბაზანა", "ოფისი", "ავტოფარეხი", "კორიდორი", "სარდაფი", "სასადილო ოთახი", "ფანჯარა", "ტერასა", "საპარსი"], - "bn": ["রান্নাঘর", "বসার ঘর", "শোনার ঘর", "বাথরুম", "অফিস", "গ্যারেজ", "বারান্দা", "বেসমেন্ট", "খাবার ঘর", "বাগান", "টেরাস", "ধোপার ঘর"], - "gu": ["રસોડું", "બેઠક", "સુવાનો ઓરડો", "બાથરૂમ", "ઓફિસ", "ગેરેજ", "બાર", "બેઝમેન્ટ", "જમવાનું ઓરડો", "બગીચો", "ટેરેસ", "ધોવાનો ઓરડો"], - "hi": ["रसोई", "बैठक", "बेडरूम", "बाथरूम", "दफ़्तर", "गैराज", "गली", "बेसमेंट", "भोजन कक्ष", "बगीचा", "बरामदा", "धोने का कमरा"], - "kn": ["ಅಡಿಗೆ ಮನೆ", "ಹಾಲ್", "ನಿದ್ರಾಕೋಶ", "ಬಾತ್ರೂಮ್", "ಕಚೇರಿ", "ಗಾರೇಜ್", "ಕಾರಿಡಾರ್", "ಬೇಸ್ಮೆಂಟ್", "ಊಟದ ಕೋಶ", "ತೋಟ", "ಟೆರೇಸ್", "ಒಗ್ಗುವ ಕೋಶ"], - "ml": ["അടുക്കളം", "കഴിക്കുന്ന മുറി", "കിടക്കയ്ക്കുള്ള മുറി", "കുളിമുറി", "ഓഫീസ്", "ഗാരേജ്", "കോറിഡോർ", "ബേസ്മെന്റ്", "ഭക്ഷണമുറി", "പൂന്തോട്ടം", "ടെറസ്", "വസ്ത്രം കഴിയുന്ന മുറി"], - "mr": ["स्वयंपाकघर", "बसण्याची खोली", "जोपायची खोली", "बाथरूम", "ऑफिस", "गॅरेज", "कॉरिडॉर", "बेसमेंट", "जेवणाची खोली", "बाग", "टेरेस", "धुण्याची खोली"], - "pa": ["ਰਸੋਈ", "ਬੈਠਕ", "ਸੌਣ ਵਾਲਾ ਕਮਰਾ", "ਬਾਥਰੂਮ", "ਦਫਤਰ", "ਗੈਰੇਜ", "ਗਲੀ", "ਬੇਸਮੈਂਟ", "ਖਾਣ ਵਾਲਾ ਕਮਰਾ", "ਬਾਗ", "ਟੈਰੇਸ", "ਧੋਣ ਵਾਲਾ ਕਮਰਾ"], - "ta": ["சமையலறை", "பெற்று அறை", "கடுக்கை அறை", "குளியலறை", "பணி இடம்", "வாகன நிறுத்தம்", "நடைபாதை", "அடித்தளம்", "உணவு அறை", "தோட்டம்", "முற்றம்", "துவைப்பு அறை"], - "te": ["వంటగది", "సభా గది", "నిద్ర గది", "స్నానగది", "కార్యాలయం", "గ్యారేజ్", "కారిడార్", "బేస్మెంట్", "భోజన గది", "తోట", "టెరస్", "ఉతికే గది"], - "ur": ["باتھ روم", "بیٹھک", "سونے کا کمرہ", "باتھ روم", "دفتر", "گیراج", "گلی", "تہہ خانہ", "کھانے کا کمرہ", "باغ", "چبوترا", "دھونے کا کمرہ"], - "mn": ["гал тогоо", "зочны өрөө", "унтлагын өрөө", "усанд орох өрөө", "оффис", "гараж", "коридор", "суурь", "хоолны өрөө", "цэцэрлэг", "терасс", "угаалгын өрөө"], - "kw": ["kek", "rom godhesi", "kewor", "rom ymolchi", "offis", "garaj", "koryor", "kelder", "rom dybri", "lowarth", "teras", "rom yowghi"], - "lb": ["Kichen", "Wunnzëmmer", "Schlofzëmmer", "Buedzëmmer", "Büro", "Garage", "Gank", "Keller", "Iesszëmmer", "Gaart", "Terrass", "Wäschkichen"], -} +_HERE = Path(__file__).resolve().parent +_BASE_LOCALE = _HERE / "base_locale" + + +def _area_names_for_lang(lang: str) -> list[str]: + """Read translated area names from ``base_locale//area.entity``.""" + path = _BASE_LOCALE / lang / "area.entity" + if not path.is_file(): + return [] + return [ + ln.strip() for ln in path.read_text(encoding="utf-8").splitlines() + if ln.strip() + ] + # --------------------------------------------------------------------------- # Template rewriting @@ -1725,16 +1673,18 @@ def _log(kind: str, intent: str, reason: str, original: str) -> None: if target.exists(): entities_written += 1 - # Seed area.entity with common names if the language is covered and - # the file does not already exist (e.g. from a materialised hassil list). + # Seed area.entity with common names if the language is covered by + # base_locale and the file does not already exist. area_entity = out / "area.entity" - if not (resume and area_entity.exists()) and lang in COMMON_AREA_NAMES: - aw = _StreamWriter(area_entity) - try: - for name in COMMON_AREA_NAMES[lang]: - aw.write(name) - finally: - aw.close() + if not (resume and area_entity.exists()): + area_names = _area_names_for_lang(lang) + if area_names: + aw = _StreamWriter(area_entity) + try: + for name in area_names: + aw.write(name) + finally: + aw.close() if owns_report: report.close() diff --git a/examples/hass-intent-dataset/export_hf_dataset.py b/examples/hass-intent-dataset/export_hf_dataset.py index 4dff955f..5d2e0989 100644 --- a/examples/hass-intent-dataset/export_hf_dataset.py +++ b/examples/hass-intent-dataset/export_hf_dataset.py @@ -46,81 +46,6 @@ SLOT_RE = re.compile(r"\{([a-zA-Z_][a-zA-Z0-9_]*)\}") VOC_RE = re.compile(r"<([a-zA-Z_][a-zA-Z0-9_]*)>") -# Domain-specific device name examples for the {name} slot -DOMAIN_DEVICE_NAMES: dict[str, list[str]] = { - "light": [ - "kitchen light", "living room light", "bedroom light", - "ceiling light", "lamp", "desk lamp", "floor lamp", - "hallway light", "bathroom light", "overhead light", - ], - "fan": [ - "ceiling fan", "bathroom fan", "kitchen fan", - "stand fan", "desk fan", "exhaust fan", - "living room fan", "bedroom fan", "tower fan", - ], - "cover": [ - "living room blinds", "bedroom curtain", "kitchen blinds", - "garage door", "front gate", "back door", - "roller shutter", "sun shade", "window blind", - ], - "lock": [ - "front door", "back door", "garage door", - "main door", "side door", "patio door", - ], - "climate": [ - "thermostat", "living room thermostat", "bedroom thermostat", - "air conditioner", "heater", "hvac", - ], - "media_player": [ - "tv", "speaker", "living room speaker", - "stereo", "soundbar", "kitchen speaker", - ], - "vacuum": [ - "vacuum", "robot vacuum", "living room vacuum", - "downstairs vacuum", "upstairs vacuum", - ], - "scene": [ - "movie night", "good night", "good morning", - "dinner time", "party mode", "relax", - ], - "script": [ - "good night routine", "morning routine", - "away mode", "arrive home", - ], - "sensor": [ - "temperature sensor", "motion sensor", "door sensor", - "humidity sensor", "window sensor", - ], - "switch": [ - "kitchen switch", "living room outlet", "hallway switch", - "porch light", "christmas lights", - ], - "water_heater": [ - "water heater", "boiler", "hot water tank", - ], - "humidifier": [ - "humidifier", "dehumidifier", "living room humidifier", - "bedroom humidifier", - ], - "homeassistant": [ - "kitchen light", "living room light", "bedroom light", - "front door", "thermostat", "tv", "ceiling fan", - "garage door", "speaker", "vacuum", - ], -} - - -def _extract_domain(intent_name: str) -> str: - """Extract sub-domain from an intent name like ``hass_light_turn_on``. - - Returns the second segment (``light``) or ``"homeassistant"`` as fallback. - """ - parts = intent_name.split("_") - if len(parts) >= 3 and parts[0] == "hass": - return parts[1] - return "homeassistant" - - def _extract_slots(template: str) -> list[str]: """Return ordered slot names from a template.""" return SLOT_RE.findall(template) @@ -153,25 +78,33 @@ def _load_locale_file(path: Path) -> list[str]: return lines +def _base_locale_dir() -> Path: + return Path(__file__).resolve().parent / "base_locale" + + def _build_slot_schema( slot_names: list[str], entity_dir: Path, lang: str = "en", - domain: str | None = None, + base_locale: Path | None = None, ) -> list[dict]: - """For each slot name, try to find a matching ``.entity`` file and load - example values. For the ``name`` slot, domain-specific device names are - only provided for English — other languages leave ``name`` as a wildcard - with empty examples unless an actual ``.entity`` file exists.""" + """For each slot name, load example values from ``base_locale//.entity`` + or fall back to ``/.entity`` from the locale tree.""" + if base_locale is None: + base_locale = _base_locale_dir() + schema: list[dict] = [] for name in slot_names: examples: list[str] = [] - if name == "name" and domain and lang.startswith("en"): - examples = DOMAIN_DEVICE_NAMES.get(domain, [])[:20] + + bl_path = base_locale / lang / f"{name}.entity" + if bl_path.is_file(): + examples = _load_locale_file(bl_path)[:20] else: entity_path = entity_dir / f"{name}.entity" if entity_path.is_file(): examples = _load_locale_file(entity_path)[:20] + schema.append({"name": name, "examples": examples}) return schema @@ -180,19 +113,29 @@ def _realise_template( template: str, slot_names: list[str], entity_dir: Path, + lang: str = "en", max_combos: int = 50, + base_locale: Path | None = None, ) -> list[tuple[str, dict[str, str | None]]]: """Generate concrete utterances by filling slots with entity values. Returns ``(utterance, slot_map)`` pairs.""" + if base_locale is None: + base_locale = _base_locale_dir() if not slot_names: return [(template, {})] - # Load value pools for each slot + # Load value pools for each slot — prefer base_locale over locale tree pools: list[list[str]] = [] for name in slot_names: - entity_path = entity_dir / f"{name}.entity" - if entity_path.is_file(): - values = _load_locale_file(entity_path) + values: list[str] = [] + bl_path = base_locale / lang / f"{name}.entity" + if bl_path.is_file(): + values = _load_locale_file(bl_path) + if not values: + entity_path = entity_dir / f"{name}.entity" + if entity_path.is_file(): + values = _load_locale_file(entity_path) + if values: pools.append(values[:10]) # cap per slot else: pools.append([f"__{name}__"]) # placeholder when no entity file @@ -227,11 +170,10 @@ def export_templates( for intent_file in sorted(lang_dir.glob("*.intent")): intent_name = intent_file.stem - sub_domain = _extract_domain(intent_name) domain = "homeassistant" for template in _load_locale_file(intent_file): slot_names = _extract_slots(template) - slots = _build_slot_schema(slot_names, entity_dir, lang=lang, domain=sub_domain) + slots = _build_slot_schema(slot_names, entity_dir, lang=lang) rows.append( { "intent_id": f"{domain}:{intent_name}", @@ -332,7 +274,7 @@ def export_test( expanded = _expand_alternations(template) for exp in expanded: slot_names = _extract_slots(exp) - realised = _realise_template(exp, slot_names, entity_dir, max_combos=20) + realised = _realise_template(exp, slot_names, entity_dir, lang=lang, max_combos=20) for utterance, slot_map in realised: if utterance in seen_utterances: continue diff --git a/examples/hass-intent-dataset/generate_base_locale.py b/examples/hass-intent-dataset/generate_base_locale.py new file mode 100644 index 00000000..6730ab10 --- /dev/null +++ b/examples/hass-intent-dataset/generate_base_locale.py @@ -0,0 +1,256 @@ +"""Generate ``base_locale/`` — the single source of truth for localized +``.entity`` files across all languages. Other scripts read from +``base_locale/`` instead of carrying hardcoded strings.""" +from __future__ import annotations + +from pathlib import Path + +# --------------------------------------------------------------------------- +# Home Assistant internal constants (same in all languages) +# --------------------------------------------------------------------------- + +HA_DOMAINS: list[str] = [ + "light", "fan", "switch", "cover", "climate", "media_player", + "sensor", "binary_sensor", "lock", "vacuum", "timer", + "input_boolean", "scene", "script", "automation", "weather", + "camera", "humidifier", "water_heater", +] + +HA_DEVICE_CLASSES: list[str] = [ + "awning", "blind", "curtain", "door", "garage", "gate", + "shade", "shutter", "window", "battery", "carbon_monoxide", + "cold", "connectivity", "gas", "heat", "light", "lock", + "moisture", "motion", "occupancy", "opening", "plug", "power", + "presence", "problem", "running", "safety", "smoke", "sound", + "tamper", "update", "vibration", +] + +HA_STATES: list[str] = ["on", "off", "open", "closed", "locked", "unlocked"] +COLORS_EN: list[str] = ["white", "black", "red", "orange", "yellow", "green", "blue", "purple", "brown", "pink", "turquoise"] + +# --------------------------------------------------------------------------- +# Translated area names (12 common rooms per language) +# --------------------------------------------------------------------------- + +COMMON_AREA_NAMES: dict[str, list[str]] = { + "en": ["kitchen", "living room", "bedroom", "bathroom", "office", "garage", "hallway", "basement", "dining room", "garden", "terrace", "laundry room"], + "de": ["Küche", "Wohnzimmer", "Schlafzimmer", "Badezimmer", "Büro", "Garage", "Flur", "Keller", "Esszimmer", "Garten", "Terrasse", "Waschküche"], + "fr": ["cuisine", "salon", "chambre", "salle de bain", "bureau", "garage", "couloir", "sous-sol", "salle à manger", "jardin", "terrasse", "buanderie"], + "es": ["cocina", "salón", "dormitorio", "baño", "oficina", "garaje", "pasillo", "sótano", "comedor", "jardín", "terraza", "lavandería"], + "pt": ["cozinha", "sala de estar", "quarto", "casa de banho", "escritório", "garagem", "corredor", "cave", "sala de jantar", "jardim", "terraço", "lavandaria"], + "pt-BR": ["cozinha", "sala", "quarto", "banheiro", "escritório", "garagem", "corredor", "porão", "sala de jantar", "jardim", "terraço", "lavanderia"], + "it": ["cucina", "soggiorno", "camera da letto", "bagno", "ufficio", "garage", "corridoio", "seminterrato", "sala da pranzo", "giardino", "terrazza", "lavanderia"], + "nl": ["keuken", "woonkamer", "slaapkamer", "badkamer", "kantoor", "garage", "gang", "kelder", "eetkamer", "tuin", "terras", "wasruimte"], + "ca": ["cuina", "sala d'estar", "dormitori", "bany", "oficina", "garatge", "passadís", "soterrani", "sala de dinar", "jardí", "terrassa", "sala de bugada"], + "da": ["køkken", "stue", "soveværelse", "badeværelse", "kontor", "garage", "gang", "kælder", "spisestue", "have", "terrasse", "vaskerum"], + "sv": ["kök", "vardagsrum", "sovrum", "badrum", "kontor", "garage", "hall", "källare", "matsal", "trädgård", "terrass", "tvättstuga"], + "nb": ["kjøkken", "stue", "soverom", "bad", "kontor", "garasje", "gang", "kjeller", "spisestue", "hage", "terrasse", "vaskerom"], + "fi": ["keittiö", "olohuone", "makuuhuone", "kylpyhuone", "toimisto", "autotalli", "käytävä", "kellari", "ruokailuhuone", "puutarha", "terassi", "pesula"], + "pl": ["kuchnia", "salon", "sypialnia", "łazienka", "biuro", "garaż", "korytarz", "piwnica", "jadalnia", "ogród", "taras", "pralnia"], + "ru": ["кухня", "гостиная", "спальня", "ванная", "офис", "гараж", "коридор", "подвал", "столовая", "сад", "терраса", "прачечная"], + "ro": ["bucătărie", "sufragerie", "dormitor", "baie", "birou", "garaj", "hol", "subsol", "sală de mese", "grădină", "terasă", "spălătorie"], + "ar": ["مطبخ", "غرفة المعيشة", "غرفة النوم", "حمام", "مكتب", "مرآب", "ممر", "قبو", "غرفة الطعام", "حديقة", "شرفة", "غرفة الغسيل"], + "he": ["מטבח", "סלון", "חדר שינה", "אמבטיה", "משרד", "מוסך", "מסדרון", "מרתף", "חדר אוכל", "גן", "מרפסת", "חדר כביסה"], + "ja": ["キッチン", "リビング", "寝室", "浴室", "書斎", "ガレージ", "廊下", "地下室", "ダイニング", "庭", "テラス", "洗濯室"], + "ko": ["부엌", "거실", "침실", "욕실", "사무실", "차고", "복도", "지하실", "식당", "정원", "테라스", "세탁실"], + "zh-CN": ["厨房", "客厅", "卧室", "浴室", "办公室", "车库", "走廊", "地下室", "餐厅", "花园", "露台", "洗衣房"], + "zh-TW": ["廚房", "客廳", "臥室", "浴室", "辦公室", "車庫", "走廊", "地下室", "餐廳", "花園", "露台", "洗衣房"], + "zh-HK": ["廚房", "客廳", "臥室", "浴室", "辦公室", "車庫", "走廊", "地下室", "餐廳", "花園", "露台", "洗衣房"], + "el": ["κουζίνα", "σαλόνι", "υπνοδωμάτιο", "μπάνιο", "γραφείο", "γκαράζ", "διάδρομος", "υπόγειο", "τραπεζαρία", "κήπος", "βεράντα", "πλυντήριο"], + "hu": ["konyha", "nappali", "hálószoba", "fürdőszoba", "iroda", "garázs", "folyosó", "pince", "étkező", "kert", "terasz", "mosókonyha"], + "cs": ["kuchyně", "obývací pokoj", "ložnice", "koupelna", "kancelář", "garáž", "chodba", "sklep", "jídelna", "zahrada", "terasa", "prádelna"], + "sk": ["kuchyňa", "obývačka", "spálňa", "kúpeľňa", "kancelária", "garáž", "chodba", "pivnica", "jedáleň", "záhrada", "terasa", "práčovňa"], + "sl": ["kuhinja", "dnevna soba", "spalnica", "kopalnica", "pisarna", "garaža", "hodnik", "klet", "jedilnica", "vrt", "terasa", "pralnica"], + "hr": ["kuhinja", "dnevni boravak", "spavaća soba", "kupaonica", "ured", "garaža", "hodnik", "podrum", "blagovaonica", "vrt", "terasa", "praonica"], + "sr": ["кухиња", "дневна соба", "спаваћа соба", "купатило", "канцеларија", "гаража", "ходник", "подрум", "трпезарија", "башта", "тераса", "вешерница"], + "sr-Latn": ["kuhinja", "dnevna soba", "spavaća soba", "kupatilo", "kancelarija", "garaža", "hodnik", "podrum", "trpezarija", "bašta", "terasa", "vešernica"], + "bg": ["кухня", "всекидневна", "спалня", "баня", "офис", "гараж", "коридор", "мазе", "трапезария", "градина", "тераса", "перално"], + "uk": ["кухня", "вітальня", "спальня", "ванна", "офіс", "гараж", "коридор", "підвал", "їдальня", "сад", "тераса", "пральня"], + "et": ["köök", "elutuba", "magamistuba", "vannituba", "kontor", "garaaž", "esik", "kelder", "söögituba", "aed", "terrass", "pesuköök"], + "lt": ["virtuvė", "svetainė", "miegamasis", "vonios kambarys", "biuras", "garažas", "koridorius", "rūsys", "valgomasis", "sodas", "terasa", "skalbimo patalpa"], + "lv": ["virtuve", "viesistaba", "guļamistaba", "vannasistaba", "kabinets", "garāža", "gaitenis", "pagrabstāvs", "ēdamistaba", "dārzs", "terase", "veļas mazgātava"], + "is": ["eldhús", "stofa", "svefnherbergi", "baðherbergi", "skrifstofa", "bílskúr", "gangur", "kjallari", "borðstofa", "garður", "verönd", "þvottahús"], + "ga": ["cistin", "seomra suí", "seomra leapa", "seomra folctha", "oifig", "garáiste", "halla", "íseallán", "seomra bia", "gairdín", "ardán", "seomra níocháin"], + "cy": ["cegin", "ystafell fyw", "ystafell wely", "ystafell molchi", "swyddfa", "garej", "coridor", "islor", "ystafell fwyta", "gardd", "teras", "ystafell golchi"], + "af": ["kombuis", "sitkamer", "slaapkamer", "badkamer", "kantoor", "motorhuis", "gang", "kelder", "eetkamer", "tuin", "terras", "wasgoedkamer"], + "sw": ["jikoni", "sebule", "chumba cha kulala", "bafuni", "ofisi", "gereji", "barabara ya ukumbi", "ghorofa ya chini", "chumba cha kulia", "bustani", "mtaro", "chumba cha kufulia"], + "eu": ["sukaldea", "egongela", "logela", "bainugela", "bulegoa", "garajea", "korridorea", "sotoa", "jangela", "lorategia", "terraza", "garbitegia"], + "gl": ["cociña", "sala de estar", "cuarto", "baño", "oficina", "garaxe", "corredor", "soto", "comedor", "xardín", "terraza", "lavandería"], + "fa": ["آشپزخانه", "اتاق نشیمن", "اتاق خواب", "حمام", "دفتر", "گاراژ", "راهرو", "زیرزمین", "اتاق ناهارخوری", "باغ", "تراس", "اتاق لباسشویی"], + "ne": ["भान्सा", "बैठक कोठा", "सुत्ने कोठा", "बाथरूम", "अफिस", "ग्यारेज", "कोरिडोर", "भूमिगत", "खाना कोठा", "बगैंचा", "टेरेस", "लुगा धुने कोठा"], + "ka": ["სამზარეულო", "მისაღები", "საძინებელი", "აბაზანა", "ოფისი", "ავტოფარეხი", "დერეფანი", "სარდაფი", "სასადილო", "ბაღი", "ტერასა", "სამრეცხაო"], + "hi": ["रसोई", "लिविंग रूम", "बेडरूम", "बाथरूम", "कार्यालय", "गैरेज", "गलियारा", "तहखाना", "भोजन कक्ष", "बगीचा", "छत", "कपड़े धोने का कमरा"], + "bn": ["রান্নাঘর", "বসার ঘর", "শোবার ঘর", "বাথরুম", "অফিস", "গ্যারেজ", "করিডোর", "বেসমেন্ট", "ডাইনিং রুম", "বাগান", "বারান্দা", "লন্ড্রি রুম"], + "gu": ["રસોડું", "લિવિંગ રૂમ", "બેડરૂમ", "બાથરૂમ", "ઓફિસ", "ગેરેજ", "કોરિડોર", "ભોંયરું", "ડાઇનિંગ રૂમ", "બગીચો", "ટેરેસ", "લોન્ડ્રી રૂમ"], + "kn": ["ಅಡುಗೆ ಮನೆ", "ದುಡಿ ಕೊಠಡಿ", "ಮಲಗು ಕೊಠಡಿ", "ಸ್ನಾನಗೃಹ", "ಕಚೇರಿ", "ಗ್ಯಾರೇಜ್", "ಕಾರಿಡಾರ್", "ನೆಲಮಾಳಿಗೆ", "ಊಟದ ಕೊಠಡಿ", "ಉದ್ಯಾನ", "ಟೆರೇಸ್", "ತೊಳೆಯುವ ಕೊಠಡಿ"], + "ml": ["അടുക്കള", "ലിവിംഗ് റൂം", "ബെഡ്‌റൂം", "കുളിമുറി", "ഓഫീസ്", "ഗാരേജ്", "ഇടനാഴി", "ബേസ്മെൻറ്", "ഡൈനിംഗ് റൂം", "പൂന്തോട്ടം", "ടെറസ്", "ലോണ്ട്രി റൂം"], + "mr": ["स्वयंपाकघर", "दिवाणखाना", "बेडरूम", "स्नानगृह", "कार्यालय", "गॅरेज", "कॉरिडॉर", "तळघर", "जेवणाची खोली", "बाग", "टेरेस", "लॉन्ड्री रूम"], + "pa": ["ਰਸੋਈ", "ਲਿਵਿੰਗ ਰੂਮ", "ਬੈੱਡਰੂਮ", "ਬਾਥਰੂਮ", "ਦਫਤਰ", "ਗੈਰਾਜ", "ਕੋਰੀਡੋਰ", "ਬੇਸਮੈਂਟ", "ਡਾਇਨਿੰਗ ਰੂਮ", "ਬਾਗ", "ਛੱਤ", "ਲਾਂਡਰੀ ਰੂਮ"], + "ta": ["சமையலறை", "பெற்று அறை", "கடுக்கை அறை", "குளியலறை", "பணி இடம்", "வாகன நிறுத்தம்", "நடைபாதை", "அடித்தளம்", "உணவு அறை", "தோட்டம்", "முற்றம்", "துவைப்பு அறை"], + "te": ["వంటగది", "సభా గది", "నిద్ర గది", "స్నానగది", "కార్యాలయం", "గ్యారేజ్", "కారిడార్", "బేస్మెంట్", "భోజన గది", "తోట", "టెరస్", "ఉతికే గది"], + "ur": ["باتھ روم", "بیٹھک", "سونے کا کمرہ", "باتھ روم", "دفتر", "گیراج", "گلی", "تہہ خانہ", "کھانے کا کمرہ", "باغ", "چبوترا", "دھونے کا کمرہ"], + "mn": ["гал тогоо", "зочны өрөө", "унтлагын өрөө", "усанд орох өрөө", "оффис", "гараж", "коридор", "суурь", "хоолны өрөө", "цэцэрлэг", "терасс", "угаалгын өрөө"], + "kw": ["kek", "rom godhesi", "kewor", "rom ymolchi", "offis", "garaj", "koryor", "kelder", "rom dybri", "lowarth", "teras", "rom yowghi"], + "lb": ["Kichen", "Wunnzëmmer", "Schlofzëmmer", "Buedzëmmer", "Büro", "Garage", "Gank", "Keller", "Iesszëmmer", "Gaart", "Terrass", "Wäschkichen"], +} + +# --------------------------------------------------------------------------- +# Translated device names per language +# --------------------------------------------------------------------------- + +DEVICE_NAMES: dict[str, list[str]] = { + "en": ["kitchen light", "living room light", "bedroom light", "ceiling fan", "thermostat", "tv", "speaker", "front door", "garage door", "vacuum"], + "pt": ["luz da cozinha", "luz da sala", "luz do quarto", "ventilador de teto", "termostato", "televisão", "altifalante", "porta da frente", "porta da garagem", "aspirador"], + "pt-BR": ["luz da cozinha", "luz da sala", "luz do quarto", "ventilador de teto", "termostato", "televisão", "caixa de som", "porta da frente", "porta da garagem", "aspirador"], + "es": ["luz de la cocina", "luz del salón", "luz del dormitorio", "ventilador de techo", "termostato", "televisor", "altavoz", "puerta principal", "puerta del garaje", "aspiradora"], + "fr": ["lumière de la cuisine", "lumière du salon", "lumière de la chambre", "ventilateur de plafond", "thermostat", "télévision", "haut-parleur", "porte d'entrée", "porte du garage", "aspirateur"], + "de": ["Küchenlicht", "Wohnzimmerlicht", "Schlafzimmerlicht", "Deckenventilator", "Thermostat", "Fernseher", "Lautsprecher", "Eingangstür", "Garagentor", "Staubsauger"], + "it": ["luce cucina", "luce soggiorno", "luce camera da letto", "ventilatore a soffitto", "termostato", "televisione", "altoparlante", "porta principale", "porta del garage", "aspirapolvere"], + "nl": ["keukenverlichting", "woonkamerverlichting", "slaapkamerverlichting", "plafondventilator", "thermostaat", "televisie", "luidspreker", "voordeur", "garagedeur", "stofzuiger"], + "pl": ["światło w kuchni", "światło w salonie", "światło w sypialni", "wentylator sufitowy", "termostat", "telewizor", "głośnik", "drzwi wejściowe", "drzwi garażowe", "odkurzacz"], + "ru": ["свет на кухне", "свет в гостиной", "свет в спальне", "потолочный вентилятор", "термостат", "телевизор", "колонка", "входная дверь", "дверь гаража", "пылесос"], + "ja": ["キッチンの照明", "リビングの照明", "寝室の照明", "シーリングファン", "サーモスタット", "テレビ", "スピーカー", "玄関", "ガレージのドア", "掃除機"], + "ar": ["ضوء المطبخ", "ضوء غرفة المعيشة", "ضوء غرفة النوم", "مروحة سقف", "منظم حرارة", "تلفاز", "مكبر صوت", "الباب الأمامي", "باب المرآب", "مكنسة"], + "zh-CN": ["厨房灯", "客厅灯", "卧室灯", "吊扇", "恒温器", "电视", "音响", "前门", "车库门", "吸尘器"], + "zh-TW": ["廚房燈", "客廳燈", "臥室燈", "吊扇", "恆溫器", "電視", "音響", "前門", "車庫門", "吸塵器"], + "zh-HK": ["廚房燈", "客廳燈", "睡房燈", "吊扇", "恆溫器", "電視", "音響", "大門", "車房門", "吸塵機"], +} + +# --------------------------------------------------------------------------- +# Translated floor / level names per language +# --------------------------------------------------------------------------- + +FLOOR_NAMES: dict[str, list[str]] = { + "en": ["ground floor", "first floor", "second floor", "basement", "attic"], + "pt": ["rés do chão", "primeiro andar", "segundo andar", "cave", "sótão"], + "pt-BR": ["térreo", "primeiro andar", "segundo andar", "porão", "sótão"], + "es": ["planta baja", "primer piso", "segundo piso", "sótano", "ático"], + "fr": ["rez-de-chaussée", "premier étage", "deuxième étage", "sous-sol", "grenier"], + "de": ["Erdgeschoss", "erster Stock", "zweiter Stock", "Keller", "Dachboden"], + "it": ["piano terra", "primo piano", "secondo piano", "seminterrato", "soffitta"], + "nl": ["begane grond", "eerste verdieping", "tweede verdieping", "kelder", "zolder"], + "pl": ["parter", "pierwsze piętro", "drugie piętro", "piwnica", "strych"], + "ru": ["первый этаж", "второй этаж", "третий этаж", "подвал", "чердак"], + "ja": ["1階", "2階", "3階", "地下室", "屋根裏"], + "ar": ["الطابق الأرضي", "الطابق الأول", "الطابق الثاني", "القبو", "العلية"], + "zh-CN": ["一楼", "二楼", "三楼", "地下室", "阁楼"], +} + +# --------------------------------------------------------------------------- +# Color and state translations (from _LANG_OVERRIDES) +# --------------------------------------------------------------------------- + +COLOR_TRANSLATIONS: dict[str, list[str]] = { + "pt": ["branco", "preto", "vermelho", "laranja", "amarelo", "verde", "azul", "roxo", "castanho", "rosa", "turquesa"], + "pt-BR": ["branco", "preto", "vermelho", "laranja", "amarelo", "verde", "azul", "roxo", "marrom", "rosa", "turquesa"], + "es": ["blanco", "negro", "rojo", "naranja", "amarillo", "verde", "azul", "púrpura", "marrón", "rosa", "turquesa"], + "fr": ["blanc", "noir", "rouge", "orange", "jaune", "vert", "bleu", "violet", "marron", "rose", "turquoise"], + "de": ["weiß", "schwarz", "rot", "orange", "gelb", "grün", "blau", "lila", "braun", "pink", "türkis"], + "it": ["bianco", "nero", "rosso", "arancione", "giallo", "verde", "blu", "viola", "marrone", "rosa", "turchese"], + "nl": ["wit", "zwart", "rood", "oranje", "geel", "groen", "blauw", "paars", "bruin", "roze", "turquoise"], + "ca": ["blanc", "negre", "vermell", "taronja", "groc", "verd", "blau", "porpra", "marró", "rosa", "turquesa"], + "da": ["hvid", "sort", "rød", "orange", "gul", "grøn", "blå", "lilla", "brun", "pink", "turkis"], + "sv": ["vit", "svart", "röd", "orange", "gul", "grön", "blå", "lila", "brun", "rosa", "turkos"], + "nb": ["hvit", "svart", "rød", "oransje", "gul", "grønn", "blå", "lilla", "brun", "rosa", "turkis"], + "fi": ["valkoinen", "musta", "punainen", "oranssi", "keltainen", "vihreä", "sininen", "violetti", "ruskea", "vaaleanpunainen", "turkoosi"], + "pl": ["biały", "czarny", "czerwony", "pomarańczowy", "żółty", "zielony", "niebieski", "fioletowy", "brązowy", "różowy", "turkusowy"], + "ru": ["белый", "чёрный", "красный", "оранжевый", "жёлтый", "зелёный", "синий", "фиолетовый", "коричневый", "розовый", "бирюзовый"], + "ja": ["白", "黒", "赤", "橙", "黄", "緑", "青", "紫", "茶", "桃", "水色"], + "ko": ["흰색", "검은색", "빨간색", "주황색", "노란색", "초록색", "파란색", "보라색", "갈색", "분홍색", "청록색"], + "zh-CN": ["白色", "黑色", "红色", "橙色", "黄色", "绿色", "蓝色", "紫色", "棕色", "粉色", "青色"], + "ar": ["أبيض", "أسود", "أحمر", "برتقالي", "أصفر", "أخضر", "أزرق", "بنفسجي", "بني", "وردي", "تركواز"], +} + +STATE_TRANSLATIONS: dict[str, list[str]] = { + "pt": ["ligado", "desligado", "aberto", "fechado", "trancado", "destrancado"], + "pt-BR": ["ligado", "desligado", "aberto", "fechado", "trancado", "destrancado"], + "es": ["encendido", "apagado", "abierto", "cerrado", "bloqueado", "desbloqueado"], + "fr": ["allumé", "éteint", "ouvert", "fermé", "verrouillé", "déverrouillé"], + "de": ["an", "aus", "offen", "geschlossen", "verriegelt", "entriegelt"], + "it": ["acceso", "spento", "aperto", "chiuso", "bloccato", "sbloccato"], + "nl": ["aan", "uit", "open", "gesloten", "vergrendeld", "ontgrendeld"], + "ca": ["encès", "apagat", "obert", "tancat", "bloquejat", "desbloquejat"], + "da": ["til", "fra", "åben", "lukket", "låst", "oplåst"], + "sv": ["på", "av", "öppen", "stängd", "låst", "olåst"], + "nb": ["på", "av", "åpen", "lukket", "låst", "opplåst"], + "fi": ["päällä", "pois", "auki", "kiinni", "lukittu", "avattu"], + "pl": ["włączony", "wyłączony", "otwarty", "zamknięty", "zamknięty", "otwarty"], + "ru": ["включено", "выключено", "открыто", "закрыто", "заблокировано", "разблокировано"], + "ja": ["オン", "オフ", "開", "閉", "施錠", "解錠"], + "ko": ["켜짐", "꺼짐", "열림", "닫힘", "잠김", "잠금해제"], + "zh-CN": ["开", "关", "打开", "关闭", "锁定", "解锁"], + "ar": ["مفعل", "معطل", "مفتوح", "مغلق", "مقفل", "مفتوح"], +} + +LANGS: list[str] = sorted({ + "en", + *COMMON_AREA_NAMES.keys(), + *DEVICE_NAMES.keys(), + *FLOOR_NAMES.keys(), + *COLOR_TRANSLATIONS.keys(), + *STATE_TRANSLATIONS.keys(), +}) + +# --------------------------------------------------------------------------- +# Generator +# --------------------------------------------------------------------------- + + +def _write_entity(lang_dir: Path, name: str, values: list[str]) -> None: + path = lang_dir / f"{name}.entity" + path.parent.mkdir(parents=True, exist_ok=True) + path.write_text("\n".join(values) + "\n", encoding="utf-8") + + +def generate_base_locale(output_dir: Path) -> int: + written = 0 + for lang in LANGS: + lang_dir = output_dir / lang + lang_dir.mkdir(parents=True, exist_ok=True) + + # area — translated + areas = COMMON_AREA_NAMES.get(lang, COMMON_AREA_NAMES["en"]) + _write_entity(lang_dir, "area", areas) + written += 1 + + # name — translated or English fallback + names = DEVICE_NAMES.get(lang, DEVICE_NAMES["en"]) + _write_entity(lang_dir, "name", names) + written += 1 + + # color — translated or English + colors = COLOR_TRANSLATIONS.get(lang, COLORS_EN) + _write_entity(lang_dir, "color", colors) + written += 1 + + # state — translated or English HA_STATES + states = STATE_TRANSLATIONS.get(lang, HA_STATES) + _write_entity(lang_dir, "state", states) + written += 1 + + # device_class / domain — HA internal identifiers (English in all langs) + _write_entity(lang_dir, "device_class", HA_DEVICE_CLASSES) + _write_entity(lang_dir, "domain", HA_DOMAINS) + written += 2 + + # floor — translated or English fallback + floors = FLOOR_NAMES.get(lang, FLOOR_NAMES["en"]) + _write_entity(lang_dir, "floor", floors) + written += 1 + + return written + + +def main() -> None: + import sys + if len(sys.argv) != 2: + print(f"Usage: {sys.argv[0]} ") + raise SystemExit(2) + out = Path(sys.argv[1]) + count = generate_base_locale(out) + n_langs = len(list(out.iterdir())) + print(f"Generated {count} .entity files across {n_langs} languages in {out}") + + +if __name__ == "__main__": + main() diff --git a/examples/hass-intent-dataset/generate_entities.py b/examples/hass-intent-dataset/generate_entities.py index dbd14e32..b5153e54 100644 --- a/examples/hass-intent-dataset/generate_entities.py +++ b/examples/hass-intent-dataset/generate_entities.py @@ -1,18 +1,19 @@ """Generate .entity files for all slots that lack them across the -hassil-locale tree. Populates language-specific value sets where -possible and falls back to English for universal HA constants.""" +hassil-locale tree. Sources localized values from the bundled +``base_locale/`` directory — the single source of truth for +translated slot values. + +Numeric slots (brightness, percentage, temperature, …) are still +generated programmatically since they are language-agnostic.""" from __future__ import annotations -import json -import os +import re from pathlib import Path -from collections import defaultdict # --------------------------------------------------------------------------- -# Slot value definitions +# Numeric ranges — same digits in every language (not "strings") # --------------------------------------------------------------------------- -# Numeric ranges — same digits in every language NUMERIC_SLOTS: dict[str, range] = { "brightness": range(0, 101), "percentage": range(0, 101), @@ -27,404 +28,71 @@ "start_minutes": range(0, 60), } -# Home Assistant internal state values (language-agnostic constants) -HA_STATES: list[str] = [ - "on", "off", "open", "closed", "locked", "unlocked", - "opening", "closing", "detected", "clear", "charging", - "not_charging", "connected", "disconnected", "home", - "away", "running", "not_running", "safe", "unsafe", - "update_available", "up_to_date", "low", "normal", - "wet", "dry", "cold", "hot", "present", "not_present", -] - -HA_DOMAINS: list[str] = [ - "light", "fan", "switch", "cover", "climate", "media_player", - "sensor", "binary_sensor", "lock", "vacuum", "timer", - "input_boolean", "scene", "script", "automation", - "weather", "camera", " humidifier", "water_heater", -] - -HA_DEVICE_CLASSES: list[str] = [ - "awning", "blind", "curtain", "door", "garage", "gate", - "shade", "shutter", "window", "battery", "carbon_monoxide", - "cold", "connectivity", "door", "garage_door", "gas", "heat", - "light", "lock", "moisture", "motion", "occupancy", "opening", - "plug", "power", "presence", "problem", "running", "safety", - "smoke", "sound", "tamper", "update", "vibration", -] - -COLORS: list[str] = [ - "white", "black", "red", "orange", "yellow", "green", - "blue", "purple", "brown", "pink", "turquoise", -] - -MEDIA_CLASSES: list[str] = [ - "artist", "album", "track", "song", "playlist", - "podcast", "movie", "tv_show", -] - -# Temporal expressions — English examples (best-effort; translators can localize) -TIMER_DURATIONS: list[str] = [ - "1 minute", "5 minutes", "10 minutes", "15 minutes", - "30 minutes", "1 hour", "2 hours", "3 hours", -] - -TIMER_STARTS: list[str] = [ - "in 1 minute", "in 5 minutes", "in 10 minutes", - "in 1 hour", "in 2 hours", "at 3 pm", "at noon", -] - -# Common device name patterns (examples; real devices are user-defined) -DEVICE_NAME_EXAMPLES: list[str] = [ - "kitchen light", "living room light", "bedroom light", - "front door", "garage door", "back gate", - "thermostat", "tv", "speaker", -] - -# Shopping / todo items -ITEM_EXAMPLES: list[str] = [ - "milk", "bread", "eggs", "coffee", "apples", -] - -# Free-form examples -MESSAGE_EXAMPLES: list[str] = [ - "hello", "dinner is ready", "the laundry is done", -] - -SEARCH_QUERY_EXAMPLES: list[str] = [ - "the beatles", "jazz", "news", -] - -CONVERSATION_COMMAND_EXAMPLES: list[str] = [ - "remind me to call mom", "set a timer", -] - -RESPONSE_EXAMPLES: list[str] = [ - "yes", "no", "ok", "sure", -] - -FLOOR_EXAMPLES: list[str] = [ - "ground floor", "first floor", "second floor", "basement", "attic", -] - # --------------------------------------------------------------------------- -# Per-language overrides (where we have translations) +# Base locale loader # --------------------------------------------------------------------------- -_LANG_OVERRIDES: dict[str, dict[str, list[str]]] = { - # Portuguese (PT) — examples - "pt": { - "state": ["ligado", "desligado", "aberto", "fechado", "trancado", "destrancado"], - "color": ["branco", "preto", "vermelho", "laranja", "amarelo", "verde", "azul", "roxo", "castanho", "rosa", "turquesa"], - }, - "pt-BR": { - "state": ["ligado", "desligado", "aberto", "fechado", "trancado", "destrancado"], - "color": ["branco", "preto", "vermelho", "laranja", "amarelo", "verde", "azul", "roxo", "marrom", "rosa", "turquesa"], - }, - "es": { - "state": ["encendido", "apagado", "abierto", "cerrado", "bloqueado", "desbloqueado"], - "color": ["blanco", "negro", "rojo", "naranja", "amarillo", "verde", "azul", "púrpura", "marrón", "rosa", "turquesa"], - }, - "fr": { - "state": ["allumé", "éteint", "ouvert", "fermé", "verrouillé", "déverrouillé"], - "color": ["blanc", "noir", "rouge", "orange", "jaune", "vert", "bleu", "violet", "marron", "rose", "turquoise"], - }, - "de": { - "state": ["an", "aus", "offen", "geschlossen", "verriegelt", "entriegelt"], - "color": ["weiß", "schwarz", "rot", "orange", "gelb", "grün", "blau", "lila", "braun", "pink", "türkis"], - }, - "it": { - "state": ["acceso", "spento", "aperto", "chiuso", "bloccato", "sbloccato"], - "color": ["bianco", "nero", "rosso", "arancione", "giallo", "verde", "blu", "viola", "marrone", "rosa", "turchese"], - }, - "nl": { - "state": ["aan", "uit", "open", "gesloten", "vergrendeld", "ontgrendeld"], - "color": ["wit", "zwart", "rood", "oranje", "geel", "groen", "blauw", "paars", "bruin", "roze", "turquoise"], - }, - "ca": { - "state": ["encès", "apagat", "obert", "tancat", "bloquejat", "desbloquejat"], - "color": ["blanc", "negre", "vermell", "taronja", "groc", "verd", "blau", "porpra", "marró", "rosa", "turquesa"], - }, - "da": { - "state": ["til", "fra", "åben", "lukket", "låst", "oplåst"], - "color": ["hvid", "sort", "rød", "orange", "gul", "grøn", "blå", "lilla", "brun", "pink", "turkis"], - }, - "sv": { - "state": ["på", "av", "öppen", "stängd", "låst", "olåst"], - "color": ["vit", "svart", "röd", "orange", "gul", "grön", "blå", "lila", "brun", "rosa", "turkos"], - }, - "nb": { - "state": ["på", "av", "åpen", "lukket", "låst", "opplåst"], - "color": ["hvit", "svart", "rød", "oransje", "gul", "grønn", "blå", "lilla", "brun", "rosa", "turkis"], - }, - "fi": { - "state": ["päällä", "pois", "auki", "kiinni", "lukittu", "avattu"], - "color": ["valkoinen", "musta", "punainen", "oranssi", "keltainen", "vihreä", "sininen", "violetti", "ruskea", "vaaleanpunainen", "turkoosi"], - }, - "pl": { - "state": ["włączony", "wyłączony", "otwarty", "zamknięty", "zamknięty", "otwarty"], - "color": ["biały", "czarny", "czerwony", "pomarańczowy", "żółty", "zielony", "niebieski", "fioletowy", "brązowy", "różowy", "turkusowy"], - }, - "ru": { - "state": ["включено", "выключено", "открыто", "закрыто", "заблокировано", "разблокировано"], - "color": ["белый", "чёрный", "красный", "оранжевый", "жёлтый", "зелёный", "синий", "фиолетовый", "коричневый", "розовый", "бирюзовый"], - }, - "ja": { - "state": ["オン", "オフ", "開", "閉", "施錠", "解錠"], - "color": ["白", "黒", "赤", "橙", "黄", "緑", "青", "紫", "茶", "桃", "水色"], - }, - "ko": { - "state": ["켜짐", "꺼짐", "열림", "닫힘", "잠김", "잠금해제"], - "color": ["흰색", "검은색", "빨간색", "주황색", "노란색", "초록색", "파란색", "보라색", "갈색", "분홍색", "청록색"], - }, - "zh-CN": { - "state": ["开", "关", "打开", "关闭", "锁定", "解锁"], - "color": ["白色", "黑色", "红色", "橙色", "黄色", "绿色", "蓝色", "紫色", "棕色", "粉色", "青色"], - }, - "ar": { - "state": ["مفعل", "معطل", "مفتوح", "مغلق", "مقفل", "مفتوح"], - "color": ["أبيض", "أسود", "أحمر", "برتقالي", "أصفر", "أخضر", "أزرق", "بنفسجي", "بني", "وردي", "تركواز"], - }, - "he": { - "state": ["דלוק", "כבוי", "פתוח", "סגור", "נעול", "פתוח"], - "color": ["לבן", "שחור", "אדום", "כתום", "צהוב", "ירוק", "כחול", "סגול", "חום", "ורוד", "טורקיז"], - }, - "tr": { - "state": ["açık", "kapalı", "açık", "kapalı", "kilitli", "kilitli açık"], - "color": ["beyaz", "siyah", "kırmızı", "turuncu", "sarı", "yeşil", "mavi", "mor", "kahverengi", "pembe", "turkuaz"], - }, - "th": { - "state": ["เปิด", "ปิด", "เปิด", "ปิด", "ล็อค", "ปลดล็อค"], - "color": ["ขาว", "ดำ", "แดง", "ส้ม", "เหลือง", "เขียว", "น้ำเงิน", "ม่วง", "น้ำตาล", "ชมพู", "ฟ้า"], - }, - "vi": { - "state": ["bật", "tắt", "mở", "đóng", "khóa", "mở khóa"], - "color": ["trắng", "đen", "đỏ", "cam", "vàng", "xanh lá", "xanh dương", "tím", "nâu", "hồng", "ngọc"], - }, - "id": { - "state": ["hidup", "mati", "terbuka", "tertutup", "terkunci", "terbuka"], - "color": ["putih", "hitam", "merah", "oranye", "kuning", "hijau", "biru", "ungu", "coklat", "merah muda", "biru toska"], - }, - "ms": { - "state": ["hidup", "mati", "buka", "tutup", "kunci", "buka"], - "color": ["putih", "hitam", "merah", "oren", "kuning", "hijau", "biru", "ungu", "perang", "merah jambu", "biru turquoise"], - }, - "ro": { - "state": ["pornit", "oprit", "deschis", "închis", "blocat", "deblocat"], - "color": ["alb", "negru", "roșu", "portocaliu", "galben", "verde", "albastru", "mov", "maro", "roz", "turcoaz"], - }, - "el": { - "state": ["ανοικτό", "κλειστό", "ανοικτό", "κλειστό", "κλειδωμένο", "ξεκλείδωτο"], - "color": ["λευκό", "μαύρο", "κόκκινο", "πορτοκαλί", "κίτρινο", "πράσινο", "μπλε", "μωβ", "καφέ", "ροζ", "τυρκουάζ"], - }, - "hu": { - "state": ["be", "ki", "nyitva", "zárva", "zárva", "nyitva"], - "color": ["fehér", "fekete", "piros", "narancssárga", "sárga", "zöld", "kék", "lila", "barna", "rózsaszín", "türkiz"], - }, - "cs": { - "state": ["zapnuto", "vypnuto", "otevřeno", "zavřeno", "zamčeno", "odemčeno"], - "color": ["bílá", "černá", "červená", "oranžová", "žlutá", "zelená", "modrá", "fialová", "hnědá", "růžová", "tyrkysová"], - }, - "sk": { - "state": ["zapnuté", "vypnuté", "otvorené", "zatvorené", "zamknuté", "odomknuté"], - "color": ["biela", "čierna", "červená", "oranžová", "žltá", "zelená", "modrá", "fialová", "hnedá", "ružová", "tyrkysová"], - }, - "sl": { - "state": ["vklopljeno", "izklopljeno", "odprto", "zaprto", "zaklenjeno", "odklenjeno"], - "color": ["bela", "črna", "rdeča", "oranžna", "rumena", "zelena", "modra", "vijolična", "rjava", "roza", "turkizna"], - }, - "hr": { - "state": ["uključeno", "isključeno", "otvoreno", "zatvoreno", "zaključano", "otključano"], - "color": ["bijela", "crna", "crvena", "narančasta", "žuta", "zelena", "plava", "ljubičasta", "smeđa", "ružičasta", "tirkizna"], - }, - "sr": { - "state": ["укључено", "искључено", "отворено", "затворено", "закључано", "откључано"], - "color": ["бела", "црна", "црвена", "наранџаста", "жута", "зелена", "плава", "љубичаста", "смеђа", "ружа", "тиркизна"], - }, - "sr-Latn": { - "state": ["uključeno", "isključeno", "otvoreno", "zatvoreno", "zaključano", "otključano"], - "color": ["bela", "crna", "crvena", "narandžasta", "žuta", "zelena", "plava", "ljubičasta", "smeđa", "ružičasta", "tirkizna"], - }, - "bg": { - "state": ["включено", "изключено", "отворено", "затворено", "заключено", "отключено"], - "color": ["бял", "черен", "червен", "оранжев", "жълт", "зелен", "син", "лилав", "кафяв", "розов", "тюркоаз"], - }, - "uk": { - "state": ["увімкнено", "вимкнено", "відкрито", "закрито", "заблоковано", "розблоковано"], - "color": ["білий", "чорний", "червоний", "помаранчевий", "жовтий", "зелений", "синій", "фіолетовий", "коричневий", "рожевий", "бірюзовий"], - }, - "et": { - "state": ["sees", "väljas", "avatud", "suletud", "lukustatud", "avatud"], - "color": ["valge", "must", "punane", "oranž", "kollane", "roheline", "sinine", "lilla", "pruun", "roosa", "türkiis"], - }, - "lt": { - "state": [["įjungta", "išjungta", "atidaryta", "uždaryta", "užrakinta", "atrakinta"]], - "color": ["balta", "juoda", "raudona", "oranžinė", "geltona", "žalia", "mėlyna", "violetinė", "rudа", "rožinė", "turkio"], - }, - "lv": { - "state": ["ieslēgts", "izslēgts", "atvērts", "aizvērts", "aizslēgts", "atslēgts"], - "color": ["balts", "melns", "sarkans", "oranžs", "dzeltens", "zaļš", "zils", "violets", "brūns", "rozā", "tirkīzs"], - }, - "is": { - "state": ["á", "af", "opið", "lokað", "læst", "opnað"], - "color": ["hvítur", "svartur", "rauður", "appelsínugulur", "gulur", "grænn", "blár", "fjólublár", "brúnn", "bleikur", "grænblár"], - }, - "ga": { - "state": ["ar", "as", "oscailte", "dúnta", "faoi ghlas", "oscailte"], - "color": ["bán", "dubh", "dearg", "oraiste", "buí", "glas", "gorm", "corcra", "donn", "bándearg", "turcais"], - }, - "cy": { - "state": ["ymlaen", "i ffwrdd", "agored", "ar gau", "wedi'i gloi", "wedi'i datgloi"], - "color": ["gwyn", "du", "coch", "oren", "melyn", "gwyrdd", "glas", "porffor", "brown", "pinc", "torcwys"], - }, - "af": { - "state": ["aan", "af", "oop", "toe", "gesluit", "oopgesluit"], - "color": ["wit", "swart", "rooi", "oranje", "geel", "groen", "blou", "pers", "bruin", "pienk", "turkoois"], - }, - "sw": { - "state": ["wazi", "zima", "funguliwa", "fungwa", "kufungwa", "kufunguliwa"], - "color": ["nyeupe", "nyeusi", "nyekundu", "machungwa", "manjano", "kijani", "bluu", "zambarau", "kahawia", "waridi", "turquoise"], - }, - "eu": { - "state": ["piztuta", "itzalita", "irekita", "itxita", "blokeatuta", "desblokeatuta"], - "color": ["zuri", "beltz", "gorri", "laranja", "hori", "berde", "urdin", "more", "marroi", "arrosa", "turkesa"], - }, - "gl": { - "state": ["encendido", "apagado", "aberto", "pechado", "bloqueado", "desbloqueado"], - "color": ["branco", "negro", "vermello", "laranxa", "amarelo", "verde", "azul", "púrpura", "marrón", "rosa", "turquesa"], - }, - "fa": { - "state": ["روشن", "خاموش", "باز", "بسته", "قفل شده", "باز شده"], - "color": ["سفید", "سیاه", "قرمز", "نارنجی", "زرد", "سبز", "آبی", "بنفش", "قهوه‌ای", "صورتی", "فیروزه‌ای"], - }, - "ne": { - "state": ["खुला", "बन्द", "खुला", "बन्द", "बन्द", "खुला"], - "color": ["सेतो", "कालो", "रातो", "सुन्तला", "पहेँलो", "हरियो", "नीलो", "प्याजी", "खैरो", "गुलाबी", "हरियो नीलो"], - }, - "ka": { - "state": ["ჩართული", "გამორთული", "გახსნილი", "დახურული", "დაკეტილი", "გახსნილი"], - "color": ["თეთრი", "შავი", "წითელი", "ნარინჯისფერი", "ყვითელი", "მწვანე", "ლურჯი", "იისფერი", "ყავისფერი", "ვარდისფერი", "ფირუზისფერი"], - }, - "bn": { - "state": ["চালু", "বন্ধ", "খোলা", "বন্ধ", "বন্ধ", "খোলা"], - "color": ["সাদা", "কালো", "লাল", "কমলা", "হলুদ", "সবুজ", "নীল", "বেগুনি", "বাদামি", "গোলাপি", "ফিরোজা"], - }, - "gu": { - "state": ["ચાલુ", "બંધ", "ખુલ્લું", "બંધ", "બંધ", "ખુલ્લું"], - "color": ["સફેદ", "કાળો", "લાલ", "નારંગી", "પીળો", "લીલો", "વાદળી", "વાયલેટ", "તપખમ", "ગુલાબી", "ફિરોઝા"], - }, - "hi": { - "state": ["चालू", "बंद", "खुला", "बंद", "बंद", "खुला"], - "color": ["सफेद", "काला", "लाल", "नारंगी", "पीला", "हरा", "नीला", "बैंगनी", "भूरा", "गुलाबी", "फिरोजा"], - }, - "kn": { - "state": ["ಆನ್", "ಆಫ್", "ತೆರೆ", "ಮುಚ್ಚು", "ಮುಚ್ಚು", "ತೆರೆ"], - "color": ["ಬಿಳಿ", "ಕಪ್ಪ", "ಕೆಂಪು", "ಕಿಟಕಿ", "ಹಳದಿ", "ಹಸಿರು", "ನೀಲಿ", "ನೇರಳೆ", "ಕಂದು", "ಗುಲಾಬಿ", "ಟರ್ಕಿಶ್"], - }, - "ml": { - "state": ["ഓൺ", "ഓഫ്", "തുറന്ന", "അടച്ച", "പൂട്ടിയ", "തുറന്ന"], - "color": ["വെള്ള", "കറുപ്പ്", "ചുവപ്പ്", "ഓറഞ്ച്", "മഞ്ഞ", "പച്ച", "നീല", "ഊദ", "തവിട്ട്", "ചുവപ്പ്", "പച്ചനീല"], - }, - "mr": { - "state": ["चालू", "बंद", "उघड", "बंद", "बंद", "उघड"], - "color": ["पांढरा", "काळा", "लाल", "केशरी", "पिवळा", "हिरवा", "निळा", "जांभळा", "तपकिरी", "गुलाबी", "फिरोजा"], - }, - "pa": { - "state": ["ਚਾਲੂ", "ਬੰਦ", "ਖੁੱਲ੍ਹਾ", "ਬੰਦ", "ਬੰਦ", "ਖੁੱਲ੍ਹਾ"], - "color": ["ਚਿੱਟਾ", "ਕਾਲਾ", "ਲਾਲ", "ਨਾਰੰਗੀ", "ਪੀਲਾ", "ਹਰਾ", "ਨੀਲਾ", "ਜਾਮਨੀ", "ਭੂਰਾ", "ਗੁਲਾਬੀ", "ਫਿਰੋਜ਼ਾ"], - }, - "ta": { - "state": ["இயக்கத்தில்", "அணை", "திறந்த", "மூடிய", "பூட்டிய", "திறந்த"], - "color": ["வெள்ளை", "கருப்பு", "சிவப்பு", "செம்மஞ்சள்", "மஞ்சள்", "பச்சை", "நீலம்", "ஊதா", "பழுப்பு", "சிவப்பு", "கடல் பச்சை"], - }, - "te": { - "state": ["ఆన్", "ఆఫ్", "తెరిచిన", "మూసిన", "మూసిన", "తెరిచిన"], - "color": ["తెలుపు", "నలుపు", "ఎరుపు", "నారింజ", "పసుపు", "ఆకుపచ్చ", "నీలం", "ఊదా", "ముదురు", "ఎరుపు", "ఆకుపచ్చ నీలం"], - }, - "ur": { - "state": ["آن", "آف", "کھلا", "بند", "بند", "کھلا"], - "color": ["سفید", "سیاہ", "سرخ", "نارنجی", "پیلا", "سبز", "نیلا", "بنفشی", "بھورا", "گلابی", "فیروزی"], - }, - "mn": { - "state": ["ассан", "унтраасан", "нээгдсэн", "хаалттай", "түгжсэн", "нээгдсэн"], - "color": ["цагаан", "хар", "улаан", "улбар шар", "шар", "ногоон", "хөх", "хөхөвтөр", "бор", "ягаан", "түрquoise"], - }, - "kw": { - "state": ["yn-mara", "marow", "ygerys", "degesys", "gwlyk", "digorys"], - "color": ["gwynn", "du", "rudh", "oren", "melyn", "glas", "glas", "glas", "gwyrdh", "gwyrdh", "glas"], - }, - "lb": { - "state": ["un", "aus", "op", "zou", "gespaart", "op"], - "color": ["wäiss", "schwaarz", "rout", "orange", "giel", "gréng", "blo", "mof", "brong", "rosa", "turkoois"], - }, - "pl": { - "state": ["włączony", "wyłączony", "otwarty", "zamknięty", "zamknięty", "otwarty"], - "color": ["biały", "czarny", "czerwony", "pomarańczowy", "żółty", "zielony", "niebieski", "fioletowy", "brązowy", "różowy", "turkusowy"], - }, -} -# Fix lt state -if "lt" in _LANG_OVERRIDES: - _LANG_OVERRIDES["lt"]["state"] = ["įjungta", "išjungta", "atidaryta", "uždaryta", "užrakinta", "atrakinta"] +def _base_locale_dir() -> Path: + return Path(__file__).resolve().parent / "base_locale" -def _get_values(slot: str, lang: str) -> list[str] | None: - """Return a value list for ``slot`` in ``lang``, or ``None`` if the slot - should remain a free-form wildcard.""" - # Check language-specific overrides first - overrides = _LANG_OVERRIDES.get(lang, {}) - if slot in overrides: - return overrides[slot] +def _read_entity(lang: str, slot: str) -> list[str] | None: + """Read values from ``base_locale//.entity``. + Returns ``None`` if the file doesn't exist.""" + path = _base_locale_dir() / lang / f"{slot}.entity" + if not path.is_file(): + return None + return [ + ln.strip() for ln in path.read_text(encoding="utf-8").splitlines() + if ln.strip() + ] + - # Numeric ranges +def _get_values(slot: str, lang: str) -> list[str] | None: + """Return a value list for ``slot`` in ``lang``. + + Resolution order: + 1. ``base_locale//.entity`` + 2. ``base_locale/en/.entity`` (English fallback for HA constants) + 3. Programmatic numeric range (if applicable) + 4. ``None`` (free-form / wildcard slot) + """ + # 1. Language-specific + values = _read_entity(lang, slot) + if values is not None: + return values + + # 2. English fallback (for HA internal constants) + if lang != "en": + values = _read_entity("en", slot) + if values is not None: + return values + + # 3. Numeric range if slot in NUMERIC_SLOTS: return [str(n) for n in NUMERIC_SLOTS[slot]] - # Universal HA constants - if slot == "state": - return HA_STATES - if slot == "domain": - return HA_DOMAINS - if slot == "device_class": - return HA_DEVICE_CLASSES - if slot == "color": - return COLORS - if slot == "media_class": - return MEDIA_CLASSES - - # Temporal (English examples as fallback) - if slot == "timer_duration": - return TIMER_DURATIONS - if slot == "timer_start": - return TIMER_STARTS + # 4. Unknown — leave as wildcard + return None - # Example-based slots (return examples rather than leaving empty) - if slot == "name": - return DEVICE_NAME_EXAMPLES - if slot == "item": - return ITEM_EXAMPLES - if slot == "message": - return MESSAGE_EXAMPLES - if slot == "search_query": - return SEARCH_QUERY_EXAMPLES - if slot == "conversation_command": - return CONVERSATION_COMMAND_EXAMPLES - if slot == "response": - return RESPONSE_EXAMPLES - if slot == "floor": - return FLOOR_EXAMPLES - # Unknown slot — leave as wildcard (no .entity file) - return None +# --------------------------------------------------------------------------- +# Driver +# --------------------------------------------------------------------------- def generate_missing_entities(locale_dir: Path) -> dict[str, int]: """Walk the locale tree and write ``.entity`` files for every slot that - appears in ``.intent`` files but has no matching ``.entity`` file.""" + appears in ``.intent`` files but has no matching ``.entity`` file. + + Values are sourced from ``base_locale/``; slots with no translation + data are left as wildcards (no file written).""" stats: dict[str, int] = {} - langs = [d for d in locale_dir.iterdir() if d.is_dir()] - for lang_dir in sorted(langs): + for lang_dir in sorted(locale_dir.iterdir()): + if not lang_dir.is_dir(): + continue lang = lang_dir.name intent_files = list(lang_dir.glob("*.intent")) if not intent_files: @@ -457,8 +125,6 @@ def generate_missing_entities(locale_dir: Path) -> dict[str, int]: return stats -import re - def main() -> None: import sys if len(sys.argv) != 2: From f5cf375083c729dd83135cdb1d0e9d0bc58b5e70 Mon Sep 17 00:00:00 2001 From: JarbasAi Date: Tue, 2 Jun 2026 15:32:02 +0100 Subject: [PATCH 08/10] feat: add inline_keywords utility for engines without .voc support MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit inline_keywords(template, expansions) replaces refs with (a|b|c) alternation groups inline — needed for engines like Padatious that don't look up .voc files at runtime. Handles nested refs recursively with configurable max_values cap. Exported from ovos_spec_tools top-level package and documented in the API reference. --- docs/api-reference.md | 6 ++++ ovos_spec_tools/__init__.py | 3 ++ ovos_spec_tools/datasets.py | 59 +++++++++++++++++++++++++++++++++++++ test/test_datasets.py | 50 +++++++++++++++++++++++++++++++ 4 files changed, 118 insertions(+) diff --git a/docs/api-reference.md b/docs/api-reference.md index 8b35cd5c..0bf1e736 100644 --- a/docs/api-reference.md +++ b/docs/api-reference.md @@ -186,6 +186,12 @@ Expand a template into concrete utterances. `expansions` is a list of `{"keyword", "values"}` dicts (as found in the `expansions` column of hassil-intents). Uses `ovos_spec_tools.expansion.expand` under the hood. +### `inline_keywords(template, expansions=None, *, flat_vocab=None, max_values=10) -> str` + +Inline ```` references as ``(v1|v2|…)`` alternation groups for engines +that don't support ``.voc`` lookups (e.g. Padatious). Handles nested references +recursively; unresolvable keywords have their brackets stripped. + ### `export_to_locale(dataset_id, lang, output_dir, *, split="train", streaming=True) -> int` Export templates from a HF dataset into an OVOS-INTENT-2 locale directory at diff --git a/ovos_spec_tools/__init__.py b/ovos_spec_tools/__init__.py index c48e821e..d0ad6d34 100644 --- a/ovos_spec_tools/__init__.py +++ b/ovos_spec_tools/__init__.py @@ -114,12 +114,14 @@ SUPPORTED_DATASETS, expand_hf_template, export_to_locale, + inline_keywords, load_dataset_templates, ) except ImportError: SUPPORTED_DATASETS = {} expand_hf_template = None # type: ignore[assignment] export_to_locale = None # type: ignore[assignment] + inline_keywords = None # type: ignore[assignment] load_dataset_templates = None # type: ignore[assignment] __all__ = [ @@ -171,6 +173,7 @@ "NamespaceTranslator", "load_dataset_templates", "expand_hf_template", + "inline_keywords", "export_to_locale", "SUPPORTED_DATASETS", "__version__", diff --git a/ovos_spec_tools/datasets.py b/ovos_spec_tools/datasets.py index ea408b5d..349b7abb 100644 --- a/ovos_spec_tools/datasets.py +++ b/ovos_spec_tools/datasets.py @@ -208,6 +208,65 @@ def expand_hf_template( return results +def inline_keywords( + template: str, + expansions: Sequence[dict] | None = None, + *, + flat_vocab: dict[str, list[str]] | None = None, + max_values: int = 10, +) -> str: + """Inline ```` references as ``(v1|v2|…)`` alternation groups. + + Engines like Padatious don't look up ``.voc`` files at runtime — they + need keywords inlined into the template body. This utility replaces + every ```` reference with its values in alternation syntax, + handling nested references recursively. + + Parameters + ---------- + template + Template string with ```` references. + expansions + List of ``{"keyword", "values"}`` dicts (from the ``expansions`` + column of the hass-intent-templates dataset). + flat_vocab + Flat ``{keyword: [values]}`` mapping, alternative to ``expansions``. + max_values + Cap the number of values per keyword inlined. Default 10. + + Returns + ------- + str + Template with all ```` references inlined. + """ + vocab: dict[str, list[str]] = {} + if flat_vocab: + vocab.update(flat_vocab) + if expansions: + for entry in expansions: + kw = entry.get("keyword", "") + vals = entry.get("values") or [] + if kw and vals: + vocab[kw] = list(vals) + + if not vocab: + return template + + def _sub(m: re.Match[str]) -> str: + vals = vocab.get(m.group(1)) + if vals: + return "(" + "|".join(vals[:max_values]) + ")" + return m.group(1) # strip brackets for unresolvable keywords + + # Iterate until stable — handles nested refs like inside + for _ in range(8): + new = VOC_RE.sub(_sub, template) + if new == template: + break + template = new + return template + + def _strip_domain(intent_id: str) -> str: """Strip the ``domain:`` prefix from an intent ID to get the base name.""" return intent_id.split(":", 1)[-1] if ":" in intent_id else intent_id diff --git a/test/test_datasets.py b/test/test_datasets.py index 2218a5b7..eed870bf 100644 --- a/test/test_datasets.py +++ b/test/test_datasets.py @@ -12,6 +12,7 @@ SUPPORTED_DATASETS, expand_hf_template, export_to_locale, + inline_keywords, load_dataset_templates, _normalize_rows, _resolve_config, @@ -190,6 +191,55 @@ def test_round_trip_small(self): assert rows[0]["template"] in intent_path.read_text() +# --------------------------------------------------------------------------- +# inline_keywords +# --------------------------------------------------------------------------- + + +class TestInlineKeywords: + def test_basic_inlining(self): + tpl = " [the] {name}" + exps = [{"keyword": "turn_on", "values": ["turn on", "switch on"]}] + assert inline_keywords(tpl, exps) == "(turn on|switch on) [the] {name}" + + def test_nested_refs(self): + tpl = " " + exps = [ + {"keyword": "broadcast", "values": ["ذع", "بلغ"]}, + {"keyword": "a", "values": ["آ", "أ"]}, + {"keyword": "everywhere", "values": ["كل مكان"]}, + ] + result = inline_keywords(tpl, exps) + assert "(آ|أ)ذع" in result or "(أ|آ)ذع" in result + assert "بلغ" in result + assert "كل مكان" in result + + def test_unresolvable_keyword(self): + tpl = " lights" + result = inline_keywords(tpl) + assert result == " lights" # no vocab → no change + + def test_flat_vocab(self): + tpl = " world" + result = inline_keywords(tpl, flat_vocab={"greet": ["hi", "hello"]}) + assert result == "(hi|hello) world" + + def test_max_values_cap(self): + tpl = "" + exps = [{"keyword": "x", "values": [str(i) for i in range(20)]}] + result = inline_keywords(tpl, exps, max_values=5) + assert "|".join(str(i) for i in range(5)) in result + assert "10" not in result + + def test_no_keyword_refs(self): + tpl = "hello world" + assert inline_keywords(tpl) == "hello world" + + def test_empty_expansions(self): + tpl = "" + assert inline_keywords(tpl, []) == "" + + # --------------------------------------------------------------------------- # SUPPORTED_DATASETS # --------------------------------------------------------------------------- From 90e22628dd97159c3cdfbbb7fb5b5bbe9e9f0c20 Mon Sep 17 00:00:00 2001 From: JarbasAi Date: Tue, 2 Jun 2026 15:35:41 +0100 Subject: [PATCH 09/10] Revert "feat: add inline_keywords utility for engines without .voc support" This reverts commit 0a34442f13c786445ea7c170e577753b2962f986. --- docs/api-reference.md | 6 ---- ovos_spec_tools/__init__.py | 3 -- ovos_spec_tools/datasets.py | 59 ------------------------------------- test/test_datasets.py | 50 ------------------------------- 4 files changed, 118 deletions(-) diff --git a/docs/api-reference.md b/docs/api-reference.md index 0bf1e736..8b35cd5c 100644 --- a/docs/api-reference.md +++ b/docs/api-reference.md @@ -186,12 +186,6 @@ Expand a template into concrete utterances. `expansions` is a list of `{"keyword", "values"}` dicts (as found in the `expansions` column of hassil-intents). Uses `ovos_spec_tools.expansion.expand` under the hood. -### `inline_keywords(template, expansions=None, *, flat_vocab=None, max_values=10) -> str` - -Inline ```` references as ``(v1|v2|…)`` alternation groups for engines -that don't support ``.voc`` lookups (e.g. Padatious). Handles nested references -recursively; unresolvable keywords have their brackets stripped. - ### `export_to_locale(dataset_id, lang, output_dir, *, split="train", streaming=True) -> int` Export templates from a HF dataset into an OVOS-INTENT-2 locale directory at diff --git a/ovos_spec_tools/__init__.py b/ovos_spec_tools/__init__.py index d0ad6d34..c48e821e 100644 --- a/ovos_spec_tools/__init__.py +++ b/ovos_spec_tools/__init__.py @@ -114,14 +114,12 @@ SUPPORTED_DATASETS, expand_hf_template, export_to_locale, - inline_keywords, load_dataset_templates, ) except ImportError: SUPPORTED_DATASETS = {} expand_hf_template = None # type: ignore[assignment] export_to_locale = None # type: ignore[assignment] - inline_keywords = None # type: ignore[assignment] load_dataset_templates = None # type: ignore[assignment] __all__ = [ @@ -173,7 +171,6 @@ "NamespaceTranslator", "load_dataset_templates", "expand_hf_template", - "inline_keywords", "export_to_locale", "SUPPORTED_DATASETS", "__version__", diff --git a/ovos_spec_tools/datasets.py b/ovos_spec_tools/datasets.py index 349b7abb..ea408b5d 100644 --- a/ovos_spec_tools/datasets.py +++ b/ovos_spec_tools/datasets.py @@ -208,65 +208,6 @@ def expand_hf_template( return results -def inline_keywords( - template: str, - expansions: Sequence[dict] | None = None, - *, - flat_vocab: dict[str, list[str]] | None = None, - max_values: int = 10, -) -> str: - """Inline ```` references as ``(v1|v2|…)`` alternation groups. - - Engines like Padatious don't look up ``.voc`` files at runtime — they - need keywords inlined into the template body. This utility replaces - every ```` reference with its values in alternation syntax, - handling nested references recursively. - - Parameters - ---------- - template - Template string with ```` references. - expansions - List of ``{"keyword", "values"}`` dicts (from the ``expansions`` - column of the hass-intent-templates dataset). - flat_vocab - Flat ``{keyword: [values]}`` mapping, alternative to ``expansions``. - max_values - Cap the number of values per keyword inlined. Default 10. - - Returns - ------- - str - Template with all ```` references inlined. - """ - vocab: dict[str, list[str]] = {} - if flat_vocab: - vocab.update(flat_vocab) - if expansions: - for entry in expansions: - kw = entry.get("keyword", "") - vals = entry.get("values") or [] - if kw and vals: - vocab[kw] = list(vals) - - if not vocab: - return template - - def _sub(m: re.Match[str]) -> str: - vals = vocab.get(m.group(1)) - if vals: - return "(" + "|".join(vals[:max_values]) + ")" - return m.group(1) # strip brackets for unresolvable keywords - - # Iterate until stable — handles nested refs like inside - for _ in range(8): - new = VOC_RE.sub(_sub, template) - if new == template: - break - template = new - return template - - def _strip_domain(intent_id: str) -> str: """Strip the ``domain:`` prefix from an intent ID to get the base name.""" return intent_id.split(":", 1)[-1] if ":" in intent_id else intent_id diff --git a/test/test_datasets.py b/test/test_datasets.py index eed870bf..2218a5b7 100644 --- a/test/test_datasets.py +++ b/test/test_datasets.py @@ -12,7 +12,6 @@ SUPPORTED_DATASETS, expand_hf_template, export_to_locale, - inline_keywords, load_dataset_templates, _normalize_rows, _resolve_config, @@ -191,55 +190,6 @@ def test_round_trip_small(self): assert rows[0]["template"] in intent_path.read_text() -# --------------------------------------------------------------------------- -# inline_keywords -# --------------------------------------------------------------------------- - - -class TestInlineKeywords: - def test_basic_inlining(self): - tpl = " [the] {name}" - exps = [{"keyword": "turn_on", "values": ["turn on", "switch on"]}] - assert inline_keywords(tpl, exps) == "(turn on|switch on) [the] {name}" - - def test_nested_refs(self): - tpl = " " - exps = [ - {"keyword": "broadcast", "values": ["ذع", "بلغ"]}, - {"keyword": "a", "values": ["آ", "أ"]}, - {"keyword": "everywhere", "values": ["كل مكان"]}, - ] - result = inline_keywords(tpl, exps) - assert "(آ|أ)ذع" in result or "(أ|آ)ذع" in result - assert "بلغ" in result - assert "كل مكان" in result - - def test_unresolvable_keyword(self): - tpl = " lights" - result = inline_keywords(tpl) - assert result == " lights" # no vocab → no change - - def test_flat_vocab(self): - tpl = " world" - result = inline_keywords(tpl, flat_vocab={"greet": ["hi", "hello"]}) - assert result == "(hi|hello) world" - - def test_max_values_cap(self): - tpl = "" - exps = [{"keyword": "x", "values": [str(i) for i in range(20)]}] - result = inline_keywords(tpl, exps, max_values=5) - assert "|".join(str(i) for i in range(5)) in result - assert "10" not in result - - def test_no_keyword_refs(self): - tpl = "hello world" - assert inline_keywords(tpl) == "hello world" - - def test_empty_expansions(self): - tpl = "" - assert inline_keywords(tpl, []) == "" - - # --------------------------------------------------------------------------- # SUPPORTED_DATASETS # --------------------------------------------------------------------------- From 8948baa238fa8f819dd08610d15cfcce28bd3b32 Mon Sep 17 00:00:00 2001 From: JarbasAi Date: Sat, 4 Jul 2026 01:22:23 +0100 Subject: [PATCH 10/10] test: run dataset tests offline and install datasets in test extra Add the datasets library to the test extra so the HF loader/export tests actually run in CI, and stub the Hub call in the round-trip test so it exercises the real loader normalization against an in-memory fixture instead of fetching a dataset over the network. Co-Authored-By: Claude Opus 4.8 (1M context) --- pyproject.toml | 2 ++ test/test_datasets.py | 24 +++++++++++++++++++++--- 2 files changed, 23 insertions(+), 3 deletions(-) diff --git a/pyproject.toml b/pyproject.toml index c7963517..6b070130 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -33,6 +33,8 @@ datasets = [ test = [ "pytest", "langcodes", + # the dataset loader/export tests exercise the HF integration for real + "datasets", ] [project.scripts] diff --git a/test/test_datasets.py b/test/test_datasets.py index 2218a5b7..ffab4bfa 100644 --- a/test/test_datasets.py +++ b/test/test_datasets.py @@ -169,9 +169,27 @@ def test_exports_structure(self): assert "Bob" in lines def test_round_trip_small(self): - """Load a single row, export, verify the intent file has the template.""" - rows = load_dataset_templates("hassil-intents", lang="en", streaming=False) - # Only keep first 3 rows to make it fast + """Load rows through the real loader (Hub call stubbed), export, and + verify the intent file carries the template.""" + raw_rows = [ + { + "intent_id": "HassTurnOn:default", + "template": " on the {name}", + "slots": [{"name": "name", "examples": ["kitchen light"]}], + "expansions": [{"keyword": "turn", "values": ["turn", "switch"]}], + }, + { + "intent_id": "HassTurnOff:default", + "template": " off the {name}", + "slots": [{"name": "name", "examples": ["kitchen light"]}], + "expansions": [{"keyword": "turn", "values": ["turn", "switch"]}], + }, + ] + + # stub the HuggingFace Hub call so the real load_dataset_templates + # normalization runs against an in-memory fixture (no network) + with patch("datasets.load_dataset", return_value=iter(raw_rows)): + rows = load_dataset_templates("hassil-intents", lang="en", streaming=False) first = [rows[0], rows[1]] with tempfile.TemporaryDirectory() as tmp: