1
0
mirror of https://github.com/JarvyJ/HomeIntent.git synced 2022-02-11 01:01:05 +03:00

Docs: Update to show how to do translations and local development (#190)

* Translation docs!

* Local development docs!

* got timer working better with translations

* fixed the `play_file` method requiring the language param.
This commit is contained in:
Jarvy Jarvison
2021-11-25 19:29:31 -06:00
committed by GitHub
parent a03a5d1287
commit 07346d0ed6
20 changed files with 486 additions and 38 deletions

View File

@@ -1,5 +1,6 @@
rhasspy:
url: http://rhasspy:12101
mqtt_host: rhasspy
externally_managed: true
home_intent:
beeps: false

View File

@@ -0,0 +1,16 @@
version: "3.9"
services:
homeintent:
image: "ghcr.io/jarvyj/homeintent:latest"
restart: unless-stopped
volumes:
- "./rhasspy:/profiles"
- "./config:/config"
- "/etc/localtime:/etc/localtime:ro"
ports:
- "11102:11102" # For the Home Intent UI
- "12183:12183" # For communicating over MQTT/satellites
- "12101:12101" # For the Rhasspy UI (optional)
devices:
- "/dev/snd:/dev/snd"

View File

@@ -1,3 +1,6 @@
# NOTE: this is the dockerfile for local development,
# if you are looking for an example docker-compose, take a look at docker-compose.example.yaml
version: "3.9"
services:
@@ -28,7 +31,7 @@ services:
- DOCKER_DEV=True
command: python3 home_intent
ui:
api:
build:
context: .
dockerfile: "development-env/Dockerfile.python"
@@ -45,6 +48,8 @@ services:
build:
context: .
dockerfile: "development-env/Dockerfile.frontend"
depends_on:
- api
volumes:
- "./ui/frontend:/usr/src/app/ui/frontend"
ports:

View File

@@ -14,24 +14,24 @@ For now, knowledge on using and setting up docker is required. In the future, di
## Installation
It is easy to get it started with a `docker-compose.yaml` file that runs Home Intent:
```docker-compose
```yaml
version: "3.9"
services:
homeintent:
image: "ghcr.io/jarvyj/homeintent:latest"
container_name: homeintent
restart: unless-stopped
volumes:
- "/PATH_TO_CONFIG/rhasspy:/profiles"
- "/PATH_TO_CONFIG/config:/config"
- "/etc/localtime:/etc/localtime:ro"
ports:
- "11102:11102"
- "12101:12101"
devices:
- "/dev/snd:/dev/snd"
image: "ghcr.io/jarvyj/homeintent:latest"
container_name: homeintent
restart: unless-stopped
volumes:
- "/PATH_TO_CONFIG/rhasspy:/profiles"
- "/PATH_TO_CONFIG/config:/config"
- "/etc/localtime:/etc/localtime:ro"
ports:
- "11102:11102" # For the Home Intent UI
- "12183:12183" # For communicating over MQTT/satellites
- "12101:12101" # For the Rhasspy UI (optional)
devices:
- "/dev/snd:/dev/snd"
```
The `/config` directory is where Home Intent configuration lives and the `/profiles` directory is where Rhasspy (the underlying speech system) stores its configs/downloads. The port `11102` exposes the Home Intent web interface, and `12101` exposes the Rhasspy interface.

17
docs/docs/humans.txt Normal file
View File

@@ -0,0 +1,17 @@
/* TEAM */
Developer: James Jarvison
/* DEDICATION */
Dedicated: To my dad, who was always skeptical. He would've loved this.
/* THANKS */
Sounds: Mark Jorgensen
Twitter: @markopolodev
GitHub: https://github.com/markopolojorgensen
/* Future translators/collab will go here */
/* SITE */
Language: English
Doctype:HTML5
IDE: Sublime Text

View File

@@ -1,4 +1,5 @@
nav:
- how-well-does-it-work.md
- Developer Reference: developer
- API Reference: api
- API Reference: api
- Translations: translations

View File

@@ -1,4 +1,10 @@
# Get File Function
!!! warning "Deprecation Notice"
This method has been moved into the [Home Intent object](./home-intent-object.md#home_intentget_file) and will be removed in Home Intent 2022.02.0. The new version follows the same semantics, but supports internationalization.
Home Intent uses a custom file loading function that will first check if a file exists in `/config` otherwise it'll load the file from the [`default_configs`](https://github.com/JarvyJ/HomeIntent/tree/main/home_intent/default_configs) folder in the source code.
This allows users to easily replace parts of components like lists of colors or sounds. This can also be useful when creating custom components, as users can add component code to `/config/custom_components` and any associated files to `/config`.

View File

@@ -17,8 +17,33 @@ Home Intent uses [pydantic](https://pydantic-docs.helpmanual.io/) models for han
## `home_intent.register(Class(), Intents)`
The `register` method expects an instantiated intent class object and an `Intents` object. This method registers the class and verifies all the slots and sentences are setup correctly. Later in the Home Intent initialization process, the registered intents are used to configure Rhasspy and setup the Intent Handler.
## `home_intent.get_file(filename, language_dependent=True)`
Home Intent uses a custom file loading function that will first check if a file exists in `/config` otherwise it'll load the file from the [`default_configs/{lang}`](https://github.com/JarvyJ/HomeIntent/tree/main/home_intent/default_configs) folder in the source code.
By default it will load a file from the corresponding language folder, but you can set `language_dependent` to `False`, and it will instead just load from `default_configs`. NOTE: currently overrides in `/config` are not language dependent.
This method allows users to easily replace parts of components like lists of colors or sounds. This can also be useful when creating custom components, as users can add component code to `/config/custom_components` and any associated files to `/config`.
```python hl_lines="7"
from home_intent import get_file
#### OTHER COMPONENT CODE #####
@intents.dictionary_slots
def color(self):
color_file = self.home_intent.get_file("home_assistant/colors.txt")
colors = color_file.read_text().strip().split("\n")
return {color: color.replace(" ", "") for color in colors}
```
In the example above, Home Intent will check for the file in `/config/home_assistant/colors.txt`, then load from the default config location in the source code: `default_configs/{lang}/home_assistant/colors.txt`.
### Conventions
When loading a file related to a component, it's best to put it in a folder that references the component's name. In the example above, files will be loaded from the `home_assistant` folder in `/config` or Home Intent's `default_configs` folder.
## `home_intent.say(str)`
Have Home Intent say something to the user. This method will likely be modified a bit when satellite support is enabled so a sentence is said at the right location.
## `home_intent.play_audio_file(filename)`
This will load a `.wav` file from the filename using the [`get_file`](./get-file.md) and play it back using Home Intent. It will also need to be modified once satellite support is in.
## `home_intent.play_audio_file(filename, language_dependent=False)`
This will load a `.wav` file from the filename using the [`get_file`](./home-intent-object.md#home_intentget_filefilename-language_dependenttrue) and play it back using Home Intent. It can also take in the `language_dependent` flag (but defaults to `False`) to load audio files specific to a language if needed.

View File

@@ -1,5 +1,6 @@
nav:
- example-component.md
- additional-homeintent-config.md
- local-development.md
- customization-dev-reference.md
- custom-rhasspy-profile.md

View File

@@ -1,5 +1,5 @@
# Example Component
Here's an example component (`timer/__init__.py`) and how it works.
Here's an example component (`timer/__init__.py`) and that could be used as the basis of creating a custom component.
```python
from collections import defaultdict
@@ -126,4 +126,6 @@ The `home_intent.get_config` function will load the `TimerSettings` from `config
The `home_intent.register` function will keep track of the instantiated object and associated intents. When the the register function is called, the slots and sentences are verified, and a bit later on in the Home Intent setup all the slot functions are called to get the slot values.
## Conventions
All builtin intents will follow the folder importing structure, so `components/<component_name>/__init__.py`. This keeps the components directory in the codebase easy to navigate and allows us to add meta information later if needed.
First party intents will follow the folder importing structure, so `components/<component_name>/__init__.py`. This keeps the components directory in the codebase easy to navigate and allows us to add meta information later if needed.
First party intents will also follow the translation structure to properly support [translations](../translations/developing-translations.md).

View File

@@ -0,0 +1,70 @@
# Local Home Intent Development
Home Intent can be developed locally by using a series of containers that have all been defined in the [`docker-compose.yaml`](https://github.com/JarvyJ/HomeIntent/blob/main/docker-compose.yaml). So now, with just a little bit of Docker knowledge, developing on Home Intent should be a lot easier.
NOTE: The examples are using `docker-compose`, newer versions of the Docker cli can use `docker compose` on Mac/Windows.
## Basic Development Setup
The development environment is setup to run from the `development-env` directory. Inside there there is a `config` folder that includes `config.example.yaml`. This is a starting `config.yaml` intended for development. Feel free to copy `config.examples.yaml` to `config.yaml` and enable integrations or add customizations to the folder.
After you have a `config.yaml` in the `development-env` directory, you can start a full development environment you can spin up by doing the following:
```
docker-compose up
```
Below mentions some of the caveats of containerized development and how to do run certain components as needed. We want to make the local development experience better, and will look for ways to do that in the future!
## Docs Development
The docs container will spin up a local mkdocs server and will hot reload as the Markdown files in the `docs` directory are changed. If you want to only work on docs, you can spin it up with the following command:
```
docker-compose up docs
```
This will start a server at [`http://localhost:8000`](http://localhost:8000) where you can see the rendered docs.
## Frontend Development
Frontend development will spin up a local node server that runs [SvelteKit](https://kit.svelte.dev/) and will hot reload as the Svelte files in `ui/frontend` are changed. It relies on the API development container to run and will spin it up automatically if it's not specified.
```
docker-compose up api frontend
```
This will start a server at [`http://localhost:3000`](http://localhost:3000) where you can see the rendered frontend.
Notably some of the Frontend/API features do not work in the containerized development environment as they rely on being deployed in the same location as Home Intent. This includes restarts and testing some of the sound functionality (which is disabled for externally managed Rhasspy in the UI).
## API Development
The API is built using [FastAPI](https://fastapi.tiangolo.com) and supports hot reload as Python files in the `ui` folder are changed.
```
docker-compose up api
```
This will start a server at [`http://localhost:11102/openapi`](http://localhost:11102/openapi)
It has the same caveats as mentioned in the Frontend Development section above. Also, if the docs/frontend are built in their respective directories, the API should host them and will be available at [`http://localhost:11102`](http://localhost:11102).
## Home Intent Development
Home Intent development is done in Python and currently **does not** support hot reload. We want to enable it, but haven't had enough time to get it working.
To start Home Intent in a container:
```
docker-compose up homeintent
```
If you make changes, you will have to `ctrl+c` out and then restart using the command above. Also, if you have spun up the entire development environment with `docker-compose up`, you can manually stop Home Intent with the following:
```
docker-compose stop homeintent
```
The Home Intent development container has the one issue of not sharing the filesystem with Rhasspy, so custom Rhasspy intent/error sounds can't be loaded. It's why `beeps` is set to `false` under `home_intent` in the development `config.yaml`.
## General Development Recommendations
When you are done doing any local development, you should probably stop whatever development containers may have been started by doing the following:
```
docker-compose stop
```
The last thing that's worth noting is that if you pull in updates from GitHub, you should run the `docker-compose up` command with the `--build` flag to rebuild any of the underlying containers.

View File

@@ -0,0 +1,155 @@
# Developing Translations
All first party simple integrations will follow the rough directory structure to properly support translations. A bit of work may be needed to convert a custom component, before being accepted into the base Home Intent.
## Directory structure
```
components
- timer
__init__.py
base_timer.py
en.py
de.py
es.py
... other languages
```
## `__init__.py`
The component will still be loaded from `__init__.py`, however, to properly support translations, there is a `import_module` method in the `home_intent` object that gets passed to the `setup` method that will load the component file associated with the user's language.
```python hl_lines="5"
from home_intent import HomeIntent, Intents
def setup(home_intent: HomeIntent):
timer = home_intent.import_module(__name__) # will load the {lang}.py
home_intent.register(timer.Timer(home_intent), timer.intents)
```
## `en.py`
The `en.py` file includes all the english (`en`) language specific sentences/code. Notably all things that can be shared across all languages should be placed in the `base_timer.py` file and imported in. This is where the `_set_timer` method is defined.
```python
from .base_timer import intents, BaseTimer
class Timer(BaseTimer):
@intents.dictionary_slots
def partial_time(self):
return {
"and [a] half": "half",
"and [a] quarter": "quarter",
"and [a] third": "third",
}
@intents.sentences(
[
"time = 0..128",
"set timer [(<time>){hours} hours] [(<time>){minutes} minutes] [(<time>){seconds} seconds]",
"set timer (<time>){hours} [($partial_time)] hours",
"set timer (<time>){minutes} [($partial_time)] minutes",
"set timer (<time>){seconds} [($partial_time)] seconds",
"set a [(<time>){hours} hour] [(<time>){minutes} minute] [(<time>){seconds} second] timer",
"set a (<time>){hours} [($partial_time)] hour timer",
"set a (<time>){minutes} [($partial_time)] minute timer",
"set a (<time>){seconds} [($partial_time)] second timer",
]
)
def set_timer(
self, hours: int = None, minutes: int = None, seconds: int = None, partial_time=None
):
human_timer_duration = self._set_timer(
"Your timer {0} has ended", hours, minutes, seconds, partial_time
)
return f"Setting timer {human_timer_duration}"
```
There is a bit of nuance here. Depending on the language the `partial_time` might not make sense, in which case it can just be omitted from the translation and not passed to `_set_timer`. On the other hand, a translation might exist for `"and a half"`, but it should still map to the english `"half"`. In the dictionary, the `half` acts more like an enum than a string. You can see that in the `get_partial_time_duration` in the `base_timer.py` code below. But in general, the value side of a dictionary slot does not need to be translated.
## `base_timer.py`
The `base_timer.py` file includes the main timer functionality and contains code that all the languages can use. In the timer constructor below, we are activating the [`humanize`](https://github.com/jmoiron/humanize) module to load the specific language settings. In this case, the `humanize` module takes care of some of the translation heavy lifting.
```python
from datetime import timedelta
from threading import Timer as ThreadingTimer
import humanize
from home_intent import HomeIntent, Intents
intents = Intents(__name__)
class TimerException(Exception):
pass
class BaseTimer:
def __init__(self, home_intent: HomeIntent):
# TODO: keep track of timers and add ability to remove timers
# self.timers = []
self.home_intent = home_intent
# for some reason the activate fails for "en", I think because it's not a "translation"
if self.home_intent.language != "en":
humanize.i18n.activate(self.home_intent.language)
def _set_timer(
self,
timer_done_message: str,
hours: int = None,
minutes: int = None,
seconds: int = None,
partial_time=None,
text_conversion_function=humanize.precisedelta,
):
timer_duration = timedelta(
hours=int(hours or 0), minutes=int(minutes or 0), seconds=int(seconds or 0),
)
if timer_duration == timedelta(0):
raise TimerException("Timer has to be set for more than 0 seconds")
if partial_time:
timer_duration = timer_duration + get_partial_time_duration(
partial_time, hours, minutes, seconds
)
human_timer_duration = text_conversion_function(timer_duration)
timer = ThreadingTimer(
timer_duration.total_seconds(),
self.complete_timer,
(human_timer_duration, timer_done_message),
)
timer.start()
return human_timer_duration
def complete_timer(self, human_timer_duration: str, timer_done_message: str):
self.home_intent.play_audio_file("timer/alarm.wav")
self.home_intent.say(timer_done_message.format(human_timer_duration))
def get_partial_time_duration(partial_time, hours=None, minutes=None, seconds=None):
partial_of = None
if hours:
partial_of = "hours"
elif minutes:
partial_of = "minutes"
elif seconds:
partial_of = "seconds"
if partial_time == "half":
return timedelta(**{partial_of: 0.5})
elif partial_time == "quarter":
return timedelta(**{partial_of: 0.25})
elif partial_time == "third":
return timedelta(**{partial_of: 1 / 3})
```
## Language Helpers
There are a couple of things that can help with language specifics including:
### `home_intent.language`
This keeps track of the two letter ISO639-1 language code and can be used as needed. In the `base_timer.py` example above, it is used in the humanize activation step in the BaseTimer constructor.
### `home_intent.get_file`
This is further explained in the Home Intent [object reference](../api/home-intent-object.md#home_intentget_filefilename-language_dependenttrue), but to quickly summarize, the `get_file` method will load a file first looking in the Home Intent [`default_configs`](https://github.com/JarvyJ/HomeIntent/tree/main/home_intent/default_configs) directory in the appropriate language code folder. Some external files are language dependent and can get loaded from there as needed.
### `home_intent.play_audio_file`
The `play_audio_file` method has also been updated to support the `language_dependent` flag to load language specific sounds. However, it defaults to `False`, so it will not look for audio files in the language code folder in `default_configs` by default.

Binary file not shown.

After

Width:  |  Height:  |  Size: 243 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 161 KiB

View File

@@ -0,0 +1,143 @@
# Translating a Component
Currently only English is supported in Home Intent. We've designed the [structure](./developing-translations.md) of the code to support multiple languages and hope that people can contribute.
Currently translating requires a bit of Docker, docker-compose, and git knowledge. We know this is a high bar for contributing, but have decided to go for it to allow Python to be fully used in the translation environment. Knowing Python is not strictly needed to help with translations, but it would be useful as translators may have to refer back to the base classes during translation.
The [developing translations guide](./developing-translations.md) and [local Home Intent development](../developer/local-development.md) would be good to read beforehand to understand how it all fits together.
## Enabling another language
To get stated with translations, you will need to pull down the latest version of the repo, and [setup the `config.yaml`](../developer/local-development.md#basic-development-setup). From there you can update the development [`docker-compose.yaml`](https://github.com/JarvyJ/HomeIntent/blob/main/docker-compose.yaml) file to set the ISO639-1 language codes in Home Intent and Rhasspy. The current supported codes are the following:
- en - English
- de - German
- es - Spanish
- fr - French
- it - Italian
- nl - Dutch
- ru - Russian
- vi - Vietnamese
- sv - Swedish
- cs - Czech
You can set the language in Home Intent with the `LANGUAGE` environment variable:
```yaml hl_lines="15"
# the other containers...
homeintent:
restart: unless-stopped
build:
context: .
dockerfile: "development-env/Dockerfile.python"
depends_on:
- rhasspy
volumes:
- "./home_intent:/usr/src/app/home_intent"
- "./development-env/config:/config"
environment:
- DOCKER_DEV=True
- LANGUAGE=de
command: python3 home_intent
# some more containers...
```
In Rhasspy, the `command`'s `profile` argument also needs to be updated to the match the language code:
```yaml hl_lines="12"
rhasspy:
image: "rhasspy/rhasspy:2.5.11"
restart: unless-stopped
volumes:
- "./development-env/rhasspy:/profiles"
- "/etc/localtime:/etc/localtime:ro"
ports:
- "12101:12101"
- "12183:12183"
devices:
- "/dev/snd:/dev/snd"
command: --user-profiles /profiles --profile de
```
Doing this will load the different language option in Rhasspy and tell Home Intent to try and load the translated version of a component. If it can't do that it will fallback to English. This might not be the long term behavior, but is currently how it is setup as very few things have been translated.
## Translating Components
To translate a component, in the components folder (which should exist after kicking off the container for the first time), there are component folders with `en.py` files. You can start by copying the `en.py` file to the language you are translating to (ex: `de.py`, `es.py`, etc), and going through and translating the sentencs/structures to the other language.
```python
from .base_timer import intents, BaseTimer
class Timer(BaseTimer):
@intents.dictionary_slots
def partial_time(self):
return {
"and [a] half": "half",
"and [a] quarter": "quarter",
"and [a] third": "third",
}
@intents.sentences(
[
"time = 0..128",
"set timer [(<time>){hours} hours] [(<time>){minutes} minutes] [(<time>){seconds} seconds]",
"set timer (<time>){hours} [($partial_time)] hours",
"set timer (<time>){minutes} [($partial_time)] minutes",
"set timer (<time>){seconds} [($partial_time)] seconds",
"set a [(<time>){hours} hour] [(<time>){minutes} minute] [(<time>){seconds} second] timer",
"set a (<time>){hours} [($partial_time)] hour timer",
"set a (<time>){minutes} [($partial_time)] minute timer",
"set a (<time>){seconds} [($partial_time)] second timer",
]
)
def set_timer(
self, hours: int = None, minutes: int = None, seconds: int = None, partial_time=None
):
return self._set_timer(hours, minutes, seconds, partial_time)
```
This is the `en.py` file from the Timer component. Rhasspy uses a [simplified JSGF](https://rhasspy.readthedocs.io/en/latest/training/#sentencesini) grammar for its sentence structure. This is the most complex component in terms of grammer:
- Most of the time there will be a `($slot_name)` which will do a substitution with a slot (dictionary or regular).
- Some of the sentences include things in brackets which are considered `[optional]`.
- A couple of the sentences use sentence "rules". You can see that with the `time = 0..128` which later gets referenced in the sentence by `(<time>){variable_name}`
These can be mixed and matched to create various types of sentence structures.
When a sentence is spoken, Rhasspy parses out specific types of variables - the `($slot_name)` and `{curly_bracket}`, which then Home Intent picks up and calls the associated function with. In the example above, `set_timer` takes in `hours`, `minutes`, and `seconds` from the `{curly_bracket}` and `partial_time` from the `($slot_name)`
## Testing Translations
After making changes to the codebase, you can start Home Intent in the Docker container:
```
docker-compose up homeintent
```
After it's running, you can go to the Rhasspy UI ([`http://localhost:12101`](http://localhost:12101)), click on the `Sentences` tab and select `intents/home_intent.ini` from the file dropdown to see the sentences and `Slots` tab to see any language specific slots.
Sentences Tab:
![sentences tab in Rhasspy](./sentences.png)
Slots Tab:
![slots tab in Rhasspy](./slots.png)
From there, you can verbally test the translations and see how well they work. If changes are needed, you can stop running Home Intent with `ctrl+c`, make further changes, and start the container again. Once they are working as expected, the files can be Pull Requested back in, and we can work on getting it merged!
## Translation Considerations
We always want to keep Home Intent working well and feeling professional, so we have some considerations that might help.
### Common Sentence Structure
In English all the sentences try to start with verbs ("Turn on the kitchen light", "Set timer 5 minutes", "Open the garage door", etc). The main idea behind this is to have a sentence structure that is consistent across all types of intents and makes the sentence usage "guessable". So the idea with any of the translations is that once folks understand the sentence structure, they should be able to guess all the variations without much effort.
### Understanding the on/off types of issues
One of the issues we run into in English voice recognition is that "on" and "off" sound very similar and mean very different things. To get around this, for (at least) Home Assistant, there's a [`prefer_toggle`](../../integrations/home-assistant.md#on-prefer_toggle) setting that toggles instead of directly performing the action that was requested. This gets Home Intent to perform the correct action even if it misheard the sentence.
I'm sure this will affect other languages. If they follow the `prefer_toggle` convention, they can use it. But if another language quirk causes "system confusion", please put an issue into the tracker so we can work to resolve it.
### Others
I'm sure while translating we may come across other language considerations that can be used to further improve the quality of Home Intent. Feel free to start a discussion or open an issue to discuss. We'd love to hear about it!
TODO: fix the timer the code to not hardcode english...

View File

@@ -22,16 +22,9 @@ class BaseTimer:
if self.home_intent.language != "en":
humanize.i18n.activate(self.home_intent.language)
@intents.dictionary_slots
def partial_time(self):
return {
"and [a] half": "half",
"and [a] quarter": "quarter",
"and [a] third": "third",
}
def _set_timer(
self,
timer_done_message: str,
hours: int = None,
minutes: int = None,
seconds: int = None,
@@ -49,14 +42,16 @@ class BaseTimer:
)
human_timer_duration = text_conversion_function(timer_duration)
timer = ThreadingTimer(
timer_duration.total_seconds(), self.complete_timer, (human_timer_duration,),
timer_duration.total_seconds(),
self.complete_timer,
(human_timer_duration, timer_done_message),
)
timer.start()
return f"Setting timer {human_timer_duration}"
return human_timer_duration
def complete_timer(self, human_timer_duration: str):
def complete_timer(self, human_timer_duration: str, timer_done_message: str):
self.home_intent.play_audio_file("timer/alarm.wav")
self.home_intent.say(f"Your timer {human_timer_duration} has ended")
self.home_intent.say(timer_done_message.format(human_timer_duration))
def get_partial_time_duration(partial_time, hours=None, minutes=None, seconds=None):

View File

@@ -2,6 +2,14 @@ from .base_timer import intents, BaseTimer
class Timer(BaseTimer):
@intents.dictionary_slots
def partial_time(self):
return {
"and [a] half": "half",
"and [a] quarter": "quarter",
"and [a] third": "third",
}
@intents.sentences(
[
"time = 0..128",
@@ -18,4 +26,7 @@ class Timer(BaseTimer):
def set_timer(
self, hours: int = None, minutes: int = None, seconds: int = None, partial_time=None
):
return self._set_timer(hours, minutes, seconds, partial_time)
human_timer_duration = self._set_timer(
"Your timer {0} has ended", hours, minutes, seconds, partial_time
)
return f"Setting timer {human_timer_duration}"

View File

@@ -126,7 +126,7 @@ class HomeIntent:
notification = {"text": text, "siteId": "default"}
self.mqtt_client.publish("hermes/tts/say", json.dumps(notification))
def play_audio_file(self, filename: str, language_dependent: bool, site_id="default"):
def play_audio_file(self, filename: str, language_dependent: bool = False, site_id="default"):
audio_file = self.get_file(filename, language_dependent=language_dependent)
if audio_file.suffix != ".wav":
raise HomeIntentException("play_audio_file currently only supports playing .wav files!")

View File

@@ -25,7 +25,7 @@ else:
def get_file(filename, relative_from=__file__, arch_dependent=False, language=None) -> PosixPath:
warnings.warn(
"get_file imported from the Home Intent package is deprecated and "
"will be removed in Home Intent 2021.02.0. "
"will be removed in Home Intent 2022.02.0. "
"Please modify your code to use get_file from the home_intent object instead "
"(ex: home_intent.get_file from the setup function)"
)

View File

@@ -2,7 +2,7 @@ import os
from pathlib import Path
from typing import Any, Dict, Optional
from pydantic import AnyHttpUrl, BaseModel, BaseSettings
from pydantic import AnyHttpUrl, BaseModel, BaseSettings, Field
import yaml
# diabled for the Settings object as pydantic passes init/env settings in
@@ -61,7 +61,7 @@ class HomeIntentSettings(BaseModel):
enable_all: bool = False
# there might be nested env var support one day: https://github.com/samuelcolvin/pydantic/pull/3159
language: str = get_env_language()
language: str = Field(default_factory=get_env_language)
class Settings(BaseSettings):