horavox 0.1.0__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- horavox-0.1.0/LICENSE +21 -0
- horavox-0.1.0/PKG-INFO +323 -0
- horavox-0.1.0/README.md +300 -0
- horavox-0.1.0/pyproject.toml +41 -0
- horavox-0.1.0/setup.cfg +4 -0
- horavox-0.1.0/src/horavox/__init__.py +1 -0
- horavox-0.1.0/src/horavox/cli.py +887 -0
- horavox-0.1.0/src/horavox/data/beep.mp3 +0 -0
- horavox-0.1.0/src/horavox/data/blank.mp3 +0 -0
- horavox-0.1.0/src/horavox/data/lang/en.json +87 -0
- horavox-0.1.0/src/horavox/data/lang/pl.json +82 -0
- horavox-0.1.0/src/horavox.egg-info/PKG-INFO +323 -0
- horavox-0.1.0/src/horavox.egg-info/SOURCES.txt +15 -0
- horavox-0.1.0/src/horavox.egg-info/dependency_links.txt +1 -0
- horavox-0.1.0/src/horavox.egg-info/entry_points.txt +2 -0
- horavox-0.1.0/src/horavox.egg-info/requires.txt +2 -0
- horavox-0.1.0/src/horavox.egg-info/top_level.txt +1 -0
horavox-0.1.0/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 Jakub T. Jankiewicz
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
horavox-0.1.0/PKG-INFO
ADDED
|
@@ -0,0 +1,323 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: horavox
|
|
3
|
+
Version: 0.1.0
|
|
4
|
+
Summary: HoraVox - a multi-language speaking clock using local AI voice models
|
|
5
|
+
Author-email: "Jakub T. Jankiewicz" <me@jcubic.pl>
|
|
6
|
+
License-Expression: MIT
|
|
7
|
+
Project-URL: Homepage, https://github.com/jcubic/horavox
|
|
8
|
+
Project-URL: Repository, https://github.com/jcubic/horavox
|
|
9
|
+
Project-URL: Issues, https://github.com/jcubic/horavox/issues
|
|
10
|
+
Keywords: tts,text-to-speech,clock,piper,speaking-clock,accessibility
|
|
11
|
+
Classifier: Development Status :: 4 - Beta
|
|
12
|
+
Classifier: Environment :: Console
|
|
13
|
+
Classifier: Intended Audience :: End Users/Desktop
|
|
14
|
+
Classifier: Operating System :: POSIX :: Linux
|
|
15
|
+
Classifier: Programming Language :: Python :: 3
|
|
16
|
+
Classifier: Topic :: Multimedia :: Sound/Audio :: Speech
|
|
17
|
+
Requires-Python: >=3.10
|
|
18
|
+
Description-Content-Type: text/markdown
|
|
19
|
+
License-File: LICENSE
|
|
20
|
+
Requires-Dist: piper-tts
|
|
21
|
+
Requires-Dist: daemonize
|
|
22
|
+
Dynamic: license-file
|
|
23
|
+
|
|
24
|
+
# HoraVox
|
|
25
|
+
|
|
26
|
+
[](https://pypi.org/project/horavox/)
|
|
27
|
+
[](https://github.com/jcubic/horavox/blob/master/LICENSE)
|
|
28
|
+
|
|
29
|
+
*Hora* (Latin: hour) + *Vox* (Latin: voice) -- the voice of the hour.
|
|
30
|
+
|
|
31
|
+
A multi-language speaking clock that announces the time using [Piper](https://github.com/rhasspy/piper) text-to-speech. It runs entirely offline using local AI voice models -- no API key or internet connection required (except for the initial voice download). It speaks the current hour on the hour using natural language idioms (e.g., "quarter past two", "wpół do czwartej") and supports any language through JSON data files.
|
|
32
|
+
|
|
33
|
+
## Features
|
|
34
|
+
|
|
35
|
+
- **Natural time idioms** -- not just "it is 14:30" but "half past two" (English) or "wpół do trzeciej" (Polish)
|
|
36
|
+
- **Classic & modern modes** -- idiomatic ("quarter past five") or digital ("five fifteen")
|
|
37
|
+
- **Multi-language** -- add a new language by creating a JSON file in `data/lang/`
|
|
38
|
+
- **Fully offline** -- uses local AI voice models, no API key or cloud service needed
|
|
39
|
+
- **Voice management** -- browse, download, and auto-detect Piper voices from Hugging Face
|
|
40
|
+
- **Bluetooth audio fix** -- plays a silent MP3 before speech to prevent clipping on Bluetooth speakers
|
|
41
|
+
- **Flexible scheduling** -- restrict announcements to a time range (e.g., 7:00--22:00)
|
|
42
|
+
- **Configurable interval** -- announce every N minutes with `--freq` (e.g., every 30 min)
|
|
43
|
+
- **Volume control** -- set volume 0--100% with `--volume`
|
|
44
|
+
- **Background mode** -- run as a daemon with `--background`, stop with `--stop`
|
|
45
|
+
- **Hour beeps** -- 2 beeps on the full hour, 1 beep on the half hour
|
|
46
|
+
- **Simulated time** -- debug with `--time HH:MM` to set a fake starting time
|
|
47
|
+
- **Silent by default** -- no terminal output unless `--verbose` is passed
|
|
48
|
+
|
|
49
|
+
## Requirements
|
|
50
|
+
|
|
51
|
+
- **Python 3.10+** and **pip**
|
|
52
|
+
- `aplay` (ALSA utils, for WAV playback) -- `sudo apt install alsa-utils`
|
|
53
|
+
- `mpg123` (for MP3 playback) -- `sudo apt install mpg123`
|
|
54
|
+
|
|
55
|
+
## Installation
|
|
56
|
+
|
|
57
|
+
### From PyPI
|
|
58
|
+
|
|
59
|
+
```bash
|
|
60
|
+
pip install horavox
|
|
61
|
+
```
|
|
62
|
+
|
|
63
|
+
This installs the `vox` command.
|
|
64
|
+
|
|
65
|
+
### From source
|
|
66
|
+
|
|
67
|
+
```bash
|
|
68
|
+
git clone https://github.com/jcubic/horavox.git
|
|
69
|
+
cd horavox
|
|
70
|
+
pip install .
|
|
71
|
+
```
|
|
72
|
+
|
|
73
|
+
This installs the `vox` command from the local source, including all dependencies.
|
|
74
|
+
|
|
75
|
+
## Usage
|
|
76
|
+
|
|
77
|
+
### Speak the current time and exit
|
|
78
|
+
|
|
79
|
+
```bash
|
|
80
|
+
vox --now
|
|
81
|
+
```
|
|
82
|
+
|
|
83
|
+
### Run as a clock (announces on the hour)
|
|
84
|
+
|
|
85
|
+
```bash
|
|
86
|
+
vox
|
|
87
|
+
```
|
|
88
|
+
|
|
89
|
+
### Switch between classic and modern time style
|
|
90
|
+
|
|
91
|
+
```bash
|
|
92
|
+
vox --mode classic # "quarter past five", "za kwadrans szósta" (default)
|
|
93
|
+
vox --mode modern # "five fifteen", "siedemnasta piętnaście"
|
|
94
|
+
```
|
|
95
|
+
|
|
96
|
+
Classic mode uses idiomatic expressions (quarters, halves, past/to) with 12-hour names. Modern mode reads the time digitally (hour + minutes) using 24-hour names in Polish.
|
|
97
|
+
|
|
98
|
+
### List available voices for a language
|
|
99
|
+
|
|
100
|
+
```bash
|
|
101
|
+
vox --list-voices --lang pl
|
|
102
|
+
vox --list-voices --lang en
|
|
103
|
+
```
|
|
104
|
+
|
|
105
|
+
### Install and use a specific voice
|
|
106
|
+
|
|
107
|
+
```bash
|
|
108
|
+
vox --voice en_US-lessac-medium
|
|
109
|
+
```
|
|
110
|
+
|
|
111
|
+
Voices are auto-downloaded from Hugging Face if not already installed.
|
|
112
|
+
|
|
113
|
+
### Limit speaking hours
|
|
114
|
+
|
|
115
|
+
```bash
|
|
116
|
+
vox --start 7 --end 22
|
|
117
|
+
vox --start 7:30 --end 22:30
|
|
118
|
+
```
|
|
119
|
+
|
|
120
|
+
Accepts `H`, `HH`, `H:MM`, or `HH:MM`. Supports midnight wrap (e.g., `--start 22 --end 6`).
|
|
121
|
+
|
|
122
|
+
### Announce every 30 minutes
|
|
123
|
+
|
|
124
|
+
```bash
|
|
125
|
+
vox --freq 30
|
|
126
|
+
```
|
|
127
|
+
|
|
128
|
+
Valid values for `--freq` must divide 60 evenly: 1, 2, 3, 4, 5, 6, 10, 12, 15, 20, 30, 60.
|
|
129
|
+
|
|
130
|
+
### Debug with simulated time
|
|
131
|
+
|
|
132
|
+
```bash
|
|
133
|
+
vox --time 16:00 --exit
|
|
134
|
+
```
|
|
135
|
+
|
|
136
|
+
### Run in the background
|
|
137
|
+
|
|
138
|
+
```bash
|
|
139
|
+
vox --lang pl --voice pl_PL-darkman-medium --start 8 --end 0 --background
|
|
140
|
+
```
|
|
141
|
+
|
|
142
|
+
Stop the background daemon:
|
|
143
|
+
|
|
144
|
+
```bash
|
|
145
|
+
vox --stop
|
|
146
|
+
```
|
|
147
|
+
|
|
148
|
+
### Set volume
|
|
149
|
+
|
|
150
|
+
```bash
|
|
151
|
+
vox --volume 50 # 50% volume
|
|
152
|
+
vox --volume 0 # silent, same as --nosound
|
|
153
|
+
```
|
|
154
|
+
|
|
155
|
+
`--nosound` is equivalent to `--volume 0` -- both skip voice loading and audio playback entirely.
|
|
156
|
+
|
|
157
|
+
### Enable log output
|
|
158
|
+
|
|
159
|
+
```bash
|
|
160
|
+
vox --verbose
|
|
161
|
+
```
|
|
162
|
+
|
|
163
|
+
### All options
|
|
164
|
+
|
|
165
|
+
```
|
|
166
|
+
usage: vox [-h] [--lang LANG] [--mode {classic,modern}] [--voice NAME]
|
|
167
|
+
[--list-voices] [--start HH:MM] [--end HH:MM] [--freq MIN]
|
|
168
|
+
[--time HH:MM] [--exit] [--now] [--background] [--stop]
|
|
169
|
+
[--verbose] [--volume PCT] [--nosound] [--debug]
|
|
170
|
+
|
|
171
|
+
HoraVox — announces the time using text-to-speech
|
|
172
|
+
|
|
173
|
+
options:
|
|
174
|
+
--lang LANG Language code, e.g. pl, en (default: from system locale)
|
|
175
|
+
--mode MODE Time style: classic (idiomatic) or modern (digital) (default: classic)
|
|
176
|
+
--voice NAME Voice name, e.g. en_US-lessac-medium (auto-downloads if missing)
|
|
177
|
+
--list-voices List available Piper voices for the current language and exit
|
|
178
|
+
--start HH:MM Start time for speaking range (default: 0:00)
|
|
179
|
+
--end HH:MM End time for speaking range (default: 23:59)
|
|
180
|
+
--freq MIN Announcement interval in minutes (default: 60)
|
|
181
|
+
--time HH:MM Set simulated start time for debugging (e.g., 16:00)
|
|
182
|
+
--exit Run once and exit (for debugging)
|
|
183
|
+
--now Speak the current time (with minutes) and exit
|
|
184
|
+
--background Run as a background daemon
|
|
185
|
+
--stop Stop the background daemon and exit
|
|
186
|
+
--verbose Show log messages (silent by default)
|
|
187
|
+
--volume PCT Volume level 0-100 percent (default: 100, 0 = no sound)
|
|
188
|
+
--nosound Same as --volume 0 — skip voice loading and audio playback
|
|
189
|
+
--debug Alias for --nosound --verbose
|
|
190
|
+
```
|
|
191
|
+
|
|
192
|
+
## Adding a new language
|
|
193
|
+
|
|
194
|
+
Create a JSON file in `data/lang/<code>.json` (e.g., `de.json` for German). The file contains two mode sections:
|
|
195
|
+
|
|
196
|
+
```json
|
|
197
|
+
{
|
|
198
|
+
"classic": {
|
|
199
|
+
"hours": ["midnight", "one o'clock", "...", "eleven o'clock"],
|
|
200
|
+
"hours_alt": ["midnight", "one", "...", "eleven"],
|
|
201
|
+
"minutes": {
|
|
202
|
+
"1": "one", "2": "two", "...": "...", "29": "twenty nine"
|
|
203
|
+
},
|
|
204
|
+
"patterns": {
|
|
205
|
+
"full_hour": "{hour}",
|
|
206
|
+
"quarter_past": "quarter past {hour_alt}",
|
|
207
|
+
"half_past": "half past {hour_alt}",
|
|
208
|
+
"quarter_to": "quarter to {next_hour_alt}",
|
|
209
|
+
"minutes_past": "{minutes} past {hour_alt}",
|
|
210
|
+
"minutes_to": "{minutes} to {next_hour_alt}"
|
|
211
|
+
}
|
|
212
|
+
},
|
|
213
|
+
"modern": {
|
|
214
|
+
"hours": ["midnight", "one o'clock", "..."],
|
|
215
|
+
"hours_alt": ["twelve", "one", "..."],
|
|
216
|
+
"minutes": {
|
|
217
|
+
"1": "oh one", "...": "...", "59": "fifty nine"
|
|
218
|
+
},
|
|
219
|
+
"patterns": {
|
|
220
|
+
"full_hour": "{hour}",
|
|
221
|
+
"time": "{hour_alt} {minutes}"
|
|
222
|
+
}
|
|
223
|
+
}
|
|
224
|
+
}
|
|
225
|
+
```
|
|
226
|
+
|
|
227
|
+
### Fields
|
|
228
|
+
|
|
229
|
+
**Classic mode** (idiomatic -- quarters, halves, past/to):
|
|
230
|
+
|
|
231
|
+
| Field | Required | Description |
|
|
232
|
+
|---|---|---|
|
|
233
|
+
| `hours` | Yes | 24 entries (index 0 = midnight, 12 = noon, etc.) used in `{hour}` and `{next_hour}` |
|
|
234
|
+
| `hours_alt` | No | 24 entries for alternate forms (e.g., genitive case). Defaults to `hours` if omitted |
|
|
235
|
+
| `minutes` | Yes | Keys `"1"` through `"29"` -- spoken forms for minute counts |
|
|
236
|
+
| `patterns` | Yes | 6 patterns: `full_hour`, `quarter_past`, `half_past`, `quarter_to`, `minutes_past`, `minutes_to` |
|
|
237
|
+
|
|
238
|
+
**Modern mode** (digital -- hour + minutes):
|
|
239
|
+
|
|
240
|
+
| Field | Required | Description |
|
|
241
|
+
|---|---|---|
|
|
242
|
+
| `hours` | Yes | 24 entries for full-hour announcements (can include "midnight", "noon") |
|
|
243
|
+
| `hours_alt` | No | 24 entries for the hour in `{hour_alt} {minutes}` patterns. Defaults to `hours` |
|
|
244
|
+
| `minutes` | Yes | Keys `"1"` through `"59"` -- spoken forms for all minute values |
|
|
245
|
+
| `patterns` | Yes | 2 patterns: `full_hour` and `time` |
|
|
246
|
+
|
|
247
|
+
### Placeholders
|
|
248
|
+
|
|
249
|
+
| Placeholder | Meaning |
|
|
250
|
+
|---|---|
|
|
251
|
+
| `{hour}` | Current hour from `hours` |
|
|
252
|
+
| `{hour_alt}` | Current hour from `hours_alt` |
|
|
253
|
+
| `{next_hour}` | Next hour from `hours` |
|
|
254
|
+
| `{next_hour_alt}` | Next hour from `hours_alt` |
|
|
255
|
+
| `{minutes}` | Minute count from `minutes` map |
|
|
256
|
+
| `{remaining}` | Minutes remaining to next hour (same source as `{minutes}`) |
|
|
257
|
+
|
|
258
|
+
### Pattern rules
|
|
259
|
+
|
|
260
|
+
| Pattern | When | Example (English) |
|
|
261
|
+
|---|---|---|
|
|
262
|
+
| `full_hour` | :00 | "three o'clock" |
|
|
263
|
+
| `quarter_past` | :15 | "quarter past three" |
|
|
264
|
+
| `half_past` | :30 | "half past three" |
|
|
265
|
+
| `quarter_to` | :45 | "quarter to four" |
|
|
266
|
+
| `minutes_past` | :01--:29 (not :15) | "ten past three" |
|
|
267
|
+
| `minutes_to` | :31--:59 (not :45) | "ten to four" |
|
|
268
|
+
|
|
269
|
+
## Project structure
|
|
270
|
+
|
|
271
|
+
```
|
|
272
|
+
src/horavox/
|
|
273
|
+
__init__.py Package init
|
|
274
|
+
cli.py Main script (installed as `vox` via pip)
|
|
275
|
+
data/
|
|
276
|
+
lang/
|
|
277
|
+
en.json English time data
|
|
278
|
+
pl.json Polish time data
|
|
279
|
+
blank.mp3 Silent MP3 for Bluetooth audio wake-up
|
|
280
|
+
beep.mp3 Beep sound for hour/half-hour signals
|
|
281
|
+
pyproject.toml Package configuration
|
|
282
|
+
|
|
283
|
+
~/.horavox/ Runtime data (created automatically)
|
|
284
|
+
voices/ Downloaded Piper voice models (.onnx)
|
|
285
|
+
cache/ Voice catalog cache + PID file
|
|
286
|
+
horavox.log Spoken words + error log
|
|
287
|
+
```
|
|
288
|
+
|
|
289
|
+
## Development
|
|
290
|
+
|
|
291
|
+
```bash
|
|
292
|
+
git clone https://github.com/jcubic/horavox.git
|
|
293
|
+
cd horavox
|
|
294
|
+
pip install -r requirements.txt
|
|
295
|
+
```
|
|
296
|
+
|
|
297
|
+
This installs only the dependencies without installing the package itself. You can then run the script directly:
|
|
298
|
+
|
|
299
|
+
```bash
|
|
300
|
+
python src/horavox/cli.py --now
|
|
301
|
+
```
|
|
302
|
+
|
|
303
|
+
Alternatively, install in editable mode to get the `vox` command that reflects your source changes:
|
|
304
|
+
|
|
305
|
+
```bash
|
|
306
|
+
pip install -e .
|
|
307
|
+
```
|
|
308
|
+
|
|
309
|
+
### Publishing to PyPI
|
|
310
|
+
|
|
311
|
+
Update the `VERSION` variable in the `Makefile`, then run:
|
|
312
|
+
|
|
313
|
+
```bash
|
|
314
|
+
make publish
|
|
315
|
+
```
|
|
316
|
+
|
|
317
|
+
This updates the version in `pyproject.toml`, `cli.py`, and `README.md`, builds the package, and uploads it to PyPI.
|
|
318
|
+
|
|
319
|
+
## License
|
|
320
|
+
|
|
321
|
+
Copyright (C) 2026 [Jakub T. Jankiewicz](https://jakub.jankiewicz.org)
|
|
322
|
+
|
|
323
|
+
Released under MIT license
|
horavox-0.1.0/README.md
ADDED
|
@@ -0,0 +1,300 @@
|
|
|
1
|
+
# HoraVox
|
|
2
|
+
|
|
3
|
+
[](https://pypi.org/project/horavox/)
|
|
4
|
+
[](https://github.com/jcubic/horavox/blob/master/LICENSE)
|
|
5
|
+
|
|
6
|
+
*Hora* (Latin: hour) + *Vox* (Latin: voice) -- the voice of the hour.
|
|
7
|
+
|
|
8
|
+
A multi-language speaking clock that announces the time using [Piper](https://github.com/rhasspy/piper) text-to-speech. It runs entirely offline using local AI voice models -- no API key or internet connection required (except for the initial voice download). It speaks the current hour on the hour using natural language idioms (e.g., "quarter past two", "wpół do czwartej") and supports any language through JSON data files.
|
|
9
|
+
|
|
10
|
+
## Features
|
|
11
|
+
|
|
12
|
+
- **Natural time idioms** -- not just "it is 14:30" but "half past two" (English) or "wpół do trzeciej" (Polish)
|
|
13
|
+
- **Classic & modern modes** -- idiomatic ("quarter past five") or digital ("five fifteen")
|
|
14
|
+
- **Multi-language** -- add a new language by creating a JSON file in `data/lang/`
|
|
15
|
+
- **Fully offline** -- uses local AI voice models, no API key or cloud service needed
|
|
16
|
+
- **Voice management** -- browse, download, and auto-detect Piper voices from Hugging Face
|
|
17
|
+
- **Bluetooth audio fix** -- plays a silent MP3 before speech to prevent clipping on Bluetooth speakers
|
|
18
|
+
- **Flexible scheduling** -- restrict announcements to a time range (e.g., 7:00--22:00)
|
|
19
|
+
- **Configurable interval** -- announce every N minutes with `--freq` (e.g., every 30 min)
|
|
20
|
+
- **Volume control** -- set volume 0--100% with `--volume`
|
|
21
|
+
- **Background mode** -- run as a daemon with `--background`, stop with `--stop`
|
|
22
|
+
- **Hour beeps** -- 2 beeps on the full hour, 1 beep on the half hour
|
|
23
|
+
- **Simulated time** -- debug with `--time HH:MM` to set a fake starting time
|
|
24
|
+
- **Silent by default** -- no terminal output unless `--verbose` is passed
|
|
25
|
+
|
|
26
|
+
## Requirements
|
|
27
|
+
|
|
28
|
+
- **Python 3.10+** and **pip**
|
|
29
|
+
- `aplay` (ALSA utils, for WAV playback) -- `sudo apt install alsa-utils`
|
|
30
|
+
- `mpg123` (for MP3 playback) -- `sudo apt install mpg123`
|
|
31
|
+
|
|
32
|
+
## Installation
|
|
33
|
+
|
|
34
|
+
### From PyPI
|
|
35
|
+
|
|
36
|
+
```bash
|
|
37
|
+
pip install horavox
|
|
38
|
+
```
|
|
39
|
+
|
|
40
|
+
This installs the `vox` command.
|
|
41
|
+
|
|
42
|
+
### From source
|
|
43
|
+
|
|
44
|
+
```bash
|
|
45
|
+
git clone https://github.com/jcubic/horavox.git
|
|
46
|
+
cd horavox
|
|
47
|
+
pip install .
|
|
48
|
+
```
|
|
49
|
+
|
|
50
|
+
This installs the `vox` command from the local source, including all dependencies.
|
|
51
|
+
|
|
52
|
+
## Usage
|
|
53
|
+
|
|
54
|
+
### Speak the current time and exit
|
|
55
|
+
|
|
56
|
+
```bash
|
|
57
|
+
vox --now
|
|
58
|
+
```
|
|
59
|
+
|
|
60
|
+
### Run as a clock (announces on the hour)
|
|
61
|
+
|
|
62
|
+
```bash
|
|
63
|
+
vox
|
|
64
|
+
```
|
|
65
|
+
|
|
66
|
+
### Switch between classic and modern time style
|
|
67
|
+
|
|
68
|
+
```bash
|
|
69
|
+
vox --mode classic # "quarter past five", "za kwadrans szósta" (default)
|
|
70
|
+
vox --mode modern # "five fifteen", "siedemnasta piętnaście"
|
|
71
|
+
```
|
|
72
|
+
|
|
73
|
+
Classic mode uses idiomatic expressions (quarters, halves, past/to) with 12-hour names. Modern mode reads the time digitally (hour + minutes) using 24-hour names in Polish.
|
|
74
|
+
|
|
75
|
+
### List available voices for a language
|
|
76
|
+
|
|
77
|
+
```bash
|
|
78
|
+
vox --list-voices --lang pl
|
|
79
|
+
vox --list-voices --lang en
|
|
80
|
+
```
|
|
81
|
+
|
|
82
|
+
### Install and use a specific voice
|
|
83
|
+
|
|
84
|
+
```bash
|
|
85
|
+
vox --voice en_US-lessac-medium
|
|
86
|
+
```
|
|
87
|
+
|
|
88
|
+
Voices are auto-downloaded from Hugging Face if not already installed.
|
|
89
|
+
|
|
90
|
+
### Limit speaking hours
|
|
91
|
+
|
|
92
|
+
```bash
|
|
93
|
+
vox --start 7 --end 22
|
|
94
|
+
vox --start 7:30 --end 22:30
|
|
95
|
+
```
|
|
96
|
+
|
|
97
|
+
Accepts `H`, `HH`, `H:MM`, or `HH:MM`. Supports midnight wrap (e.g., `--start 22 --end 6`).
|
|
98
|
+
|
|
99
|
+
### Announce every 30 minutes
|
|
100
|
+
|
|
101
|
+
```bash
|
|
102
|
+
vox --freq 30
|
|
103
|
+
```
|
|
104
|
+
|
|
105
|
+
Valid values for `--freq` must divide 60 evenly: 1, 2, 3, 4, 5, 6, 10, 12, 15, 20, 30, 60.
|
|
106
|
+
|
|
107
|
+
### Debug with simulated time
|
|
108
|
+
|
|
109
|
+
```bash
|
|
110
|
+
vox --time 16:00 --exit
|
|
111
|
+
```
|
|
112
|
+
|
|
113
|
+
### Run in the background
|
|
114
|
+
|
|
115
|
+
```bash
|
|
116
|
+
vox --lang pl --voice pl_PL-darkman-medium --start 8 --end 0 --background
|
|
117
|
+
```
|
|
118
|
+
|
|
119
|
+
Stop the background daemon:
|
|
120
|
+
|
|
121
|
+
```bash
|
|
122
|
+
vox --stop
|
|
123
|
+
```
|
|
124
|
+
|
|
125
|
+
### Set volume
|
|
126
|
+
|
|
127
|
+
```bash
|
|
128
|
+
vox --volume 50 # 50% volume
|
|
129
|
+
vox --volume 0 # silent, same as --nosound
|
|
130
|
+
```
|
|
131
|
+
|
|
132
|
+
`--nosound` is equivalent to `--volume 0` -- both skip voice loading and audio playback entirely.
|
|
133
|
+
|
|
134
|
+
### Enable log output
|
|
135
|
+
|
|
136
|
+
```bash
|
|
137
|
+
vox --verbose
|
|
138
|
+
```
|
|
139
|
+
|
|
140
|
+
### All options
|
|
141
|
+
|
|
142
|
+
```
|
|
143
|
+
usage: vox [-h] [--lang LANG] [--mode {classic,modern}] [--voice NAME]
|
|
144
|
+
[--list-voices] [--start HH:MM] [--end HH:MM] [--freq MIN]
|
|
145
|
+
[--time HH:MM] [--exit] [--now] [--background] [--stop]
|
|
146
|
+
[--verbose] [--volume PCT] [--nosound] [--debug]
|
|
147
|
+
|
|
148
|
+
HoraVox — announces the time using text-to-speech
|
|
149
|
+
|
|
150
|
+
options:
|
|
151
|
+
--lang LANG Language code, e.g. pl, en (default: from system locale)
|
|
152
|
+
--mode MODE Time style: classic (idiomatic) or modern (digital) (default: classic)
|
|
153
|
+
--voice NAME Voice name, e.g. en_US-lessac-medium (auto-downloads if missing)
|
|
154
|
+
--list-voices List available Piper voices for the current language and exit
|
|
155
|
+
--start HH:MM Start time for speaking range (default: 0:00)
|
|
156
|
+
--end HH:MM End time for speaking range (default: 23:59)
|
|
157
|
+
--freq MIN Announcement interval in minutes (default: 60)
|
|
158
|
+
--time HH:MM Set simulated start time for debugging (e.g., 16:00)
|
|
159
|
+
--exit Run once and exit (for debugging)
|
|
160
|
+
--now Speak the current time (with minutes) and exit
|
|
161
|
+
--background Run as a background daemon
|
|
162
|
+
--stop Stop the background daemon and exit
|
|
163
|
+
--verbose Show log messages (silent by default)
|
|
164
|
+
--volume PCT Volume level 0-100 percent (default: 100, 0 = no sound)
|
|
165
|
+
--nosound Same as --volume 0 — skip voice loading and audio playback
|
|
166
|
+
--debug Alias for --nosound --verbose
|
|
167
|
+
```
|
|
168
|
+
|
|
169
|
+
## Adding a new language
|
|
170
|
+
|
|
171
|
+
Create a JSON file in `data/lang/<code>.json` (e.g., `de.json` for German). The file contains two mode sections:
|
|
172
|
+
|
|
173
|
+
```json
|
|
174
|
+
{
|
|
175
|
+
"classic": {
|
|
176
|
+
"hours": ["midnight", "one o'clock", "...", "eleven o'clock"],
|
|
177
|
+
"hours_alt": ["midnight", "one", "...", "eleven"],
|
|
178
|
+
"minutes": {
|
|
179
|
+
"1": "one", "2": "two", "...": "...", "29": "twenty nine"
|
|
180
|
+
},
|
|
181
|
+
"patterns": {
|
|
182
|
+
"full_hour": "{hour}",
|
|
183
|
+
"quarter_past": "quarter past {hour_alt}",
|
|
184
|
+
"half_past": "half past {hour_alt}",
|
|
185
|
+
"quarter_to": "quarter to {next_hour_alt}",
|
|
186
|
+
"minutes_past": "{minutes} past {hour_alt}",
|
|
187
|
+
"minutes_to": "{minutes} to {next_hour_alt}"
|
|
188
|
+
}
|
|
189
|
+
},
|
|
190
|
+
"modern": {
|
|
191
|
+
"hours": ["midnight", "one o'clock", "..."],
|
|
192
|
+
"hours_alt": ["twelve", "one", "..."],
|
|
193
|
+
"minutes": {
|
|
194
|
+
"1": "oh one", "...": "...", "59": "fifty nine"
|
|
195
|
+
},
|
|
196
|
+
"patterns": {
|
|
197
|
+
"full_hour": "{hour}",
|
|
198
|
+
"time": "{hour_alt} {minutes}"
|
|
199
|
+
}
|
|
200
|
+
}
|
|
201
|
+
}
|
|
202
|
+
```
|
|
203
|
+
|
|
204
|
+
### Fields
|
|
205
|
+
|
|
206
|
+
**Classic mode** (idiomatic -- quarters, halves, past/to):
|
|
207
|
+
|
|
208
|
+
| Field | Required | Description |
|
|
209
|
+
|---|---|---|
|
|
210
|
+
| `hours` | Yes | 24 entries (index 0 = midnight, 12 = noon, etc.) used in `{hour}` and `{next_hour}` |
|
|
211
|
+
| `hours_alt` | No | 24 entries for alternate forms (e.g., genitive case). Defaults to `hours` if omitted |
|
|
212
|
+
| `minutes` | Yes | Keys `"1"` through `"29"` -- spoken forms for minute counts |
|
|
213
|
+
| `patterns` | Yes | 6 patterns: `full_hour`, `quarter_past`, `half_past`, `quarter_to`, `minutes_past`, `minutes_to` |
|
|
214
|
+
|
|
215
|
+
**Modern mode** (digital -- hour + minutes):
|
|
216
|
+
|
|
217
|
+
| Field | Required | Description |
|
|
218
|
+
|---|---|---|
|
|
219
|
+
| `hours` | Yes | 24 entries for full-hour announcements (can include "midnight", "noon") |
|
|
220
|
+
| `hours_alt` | No | 24 entries for the hour in `{hour_alt} {minutes}` patterns. Defaults to `hours` |
|
|
221
|
+
| `minutes` | Yes | Keys `"1"` through `"59"` -- spoken forms for all minute values |
|
|
222
|
+
| `patterns` | Yes | 2 patterns: `full_hour` and `time` |
|
|
223
|
+
|
|
224
|
+
### Placeholders
|
|
225
|
+
|
|
226
|
+
| Placeholder | Meaning |
|
|
227
|
+
|---|---|
|
|
228
|
+
| `{hour}` | Current hour from `hours` |
|
|
229
|
+
| `{hour_alt}` | Current hour from `hours_alt` |
|
|
230
|
+
| `{next_hour}` | Next hour from `hours` |
|
|
231
|
+
| `{next_hour_alt}` | Next hour from `hours_alt` |
|
|
232
|
+
| `{minutes}` | Minute count from `minutes` map |
|
|
233
|
+
| `{remaining}` | Minutes remaining to next hour (same source as `{minutes}`) |
|
|
234
|
+
|
|
235
|
+
### Pattern rules
|
|
236
|
+
|
|
237
|
+
| Pattern | When | Example (English) |
|
|
238
|
+
|---|---|---|
|
|
239
|
+
| `full_hour` | :00 | "three o'clock" |
|
|
240
|
+
| `quarter_past` | :15 | "quarter past three" |
|
|
241
|
+
| `half_past` | :30 | "half past three" |
|
|
242
|
+
| `quarter_to` | :45 | "quarter to four" |
|
|
243
|
+
| `minutes_past` | :01--:29 (not :15) | "ten past three" |
|
|
244
|
+
| `minutes_to` | :31--:59 (not :45) | "ten to four" |
|
|
245
|
+
|
|
246
|
+
## Project structure
|
|
247
|
+
|
|
248
|
+
```
|
|
249
|
+
src/horavox/
|
|
250
|
+
__init__.py Package init
|
|
251
|
+
cli.py Main script (installed as `vox` via pip)
|
|
252
|
+
data/
|
|
253
|
+
lang/
|
|
254
|
+
en.json English time data
|
|
255
|
+
pl.json Polish time data
|
|
256
|
+
blank.mp3 Silent MP3 for Bluetooth audio wake-up
|
|
257
|
+
beep.mp3 Beep sound for hour/half-hour signals
|
|
258
|
+
pyproject.toml Package configuration
|
|
259
|
+
|
|
260
|
+
~/.horavox/ Runtime data (created automatically)
|
|
261
|
+
voices/ Downloaded Piper voice models (.onnx)
|
|
262
|
+
cache/ Voice catalog cache + PID file
|
|
263
|
+
horavox.log Spoken words + error log
|
|
264
|
+
```
|
|
265
|
+
|
|
266
|
+
## Development
|
|
267
|
+
|
|
268
|
+
```bash
|
|
269
|
+
git clone https://github.com/jcubic/horavox.git
|
|
270
|
+
cd horavox
|
|
271
|
+
pip install -r requirements.txt
|
|
272
|
+
```
|
|
273
|
+
|
|
274
|
+
This installs only the dependencies without installing the package itself. You can then run the script directly:
|
|
275
|
+
|
|
276
|
+
```bash
|
|
277
|
+
python src/horavox/cli.py --now
|
|
278
|
+
```
|
|
279
|
+
|
|
280
|
+
Alternatively, install in editable mode to get the `vox` command that reflects your source changes:
|
|
281
|
+
|
|
282
|
+
```bash
|
|
283
|
+
pip install -e .
|
|
284
|
+
```
|
|
285
|
+
|
|
286
|
+
### Publishing to PyPI
|
|
287
|
+
|
|
288
|
+
Update the `VERSION` variable in the `Makefile`, then run:
|
|
289
|
+
|
|
290
|
+
```bash
|
|
291
|
+
make publish
|
|
292
|
+
```
|
|
293
|
+
|
|
294
|
+
This updates the version in `pyproject.toml`, `cli.py`, and `README.md`, builds the package, and uploads it to PyPI.
|
|
295
|
+
|
|
296
|
+
## License
|
|
297
|
+
|
|
298
|
+
Copyright (C) 2026 [Jakub T. Jankiewicz](https://jakub.jankiewicz.org)
|
|
299
|
+
|
|
300
|
+
Released under MIT license
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
[build-system]
|
|
2
|
+
requires = ["setuptools>=61.0"]
|
|
3
|
+
build-backend = "setuptools.build_meta"
|
|
4
|
+
|
|
5
|
+
[project]
|
|
6
|
+
name = "horavox"
|
|
7
|
+
version = "0.1.0"
|
|
8
|
+
description = "HoraVox - a multi-language speaking clock using local AI voice models"
|
|
9
|
+
readme = "README.md"
|
|
10
|
+
license = "MIT"
|
|
11
|
+
requires-python = ">=3.10"
|
|
12
|
+
authors = [
|
|
13
|
+
{name = "Jakub T. Jankiewicz", email = "me@jcubic.pl"},
|
|
14
|
+
]
|
|
15
|
+
keywords = ["tts", "text-to-speech", "clock", "piper", "speaking-clock", "accessibility"]
|
|
16
|
+
classifiers = [
|
|
17
|
+
"Development Status :: 4 - Beta",
|
|
18
|
+
"Environment :: Console",
|
|
19
|
+
"Intended Audience :: End Users/Desktop",
|
|
20
|
+
"Operating System :: POSIX :: Linux",
|
|
21
|
+
"Programming Language :: Python :: 3",
|
|
22
|
+
"Topic :: Multimedia :: Sound/Audio :: Speech",
|
|
23
|
+
]
|
|
24
|
+
dependencies = [
|
|
25
|
+
"piper-tts",
|
|
26
|
+
"daemonize",
|
|
27
|
+
]
|
|
28
|
+
|
|
29
|
+
[project.urls]
|
|
30
|
+
Homepage = "https://github.com/jcubic/horavox"
|
|
31
|
+
Repository = "https://github.com/jcubic/horavox"
|
|
32
|
+
Issues = "https://github.com/jcubic/horavox/issues"
|
|
33
|
+
|
|
34
|
+
[project.scripts]
|
|
35
|
+
vox = "horavox.cli:main"
|
|
36
|
+
|
|
37
|
+
[tool.setuptools.packages.find]
|
|
38
|
+
where = ["src"]
|
|
39
|
+
|
|
40
|
+
[tool.setuptools.package-data]
|
|
41
|
+
horavox = ["data/**/*"]
|
horavox-0.1.0/setup.cfg
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
from horavox.cli import main, __version__
|