hastty 0.1.1__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
hastty-0.1.1/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 jmleclercq
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.
hastty-0.1.1/PKG-INFO ADDED
@@ -0,0 +1,196 @@
1
+ Metadata-Version: 2.4
2
+ Name: hastty
3
+ Version: 0.1.1
4
+ Summary: A terminal (TUI) viewer for your Home Assistant dashboards, with keyboard shortcuts to trigger commands.
5
+ Author: jmleclercq
6
+ License: MIT
7
+ Project-URL: Homepage, https://github.com/jmleclercq/hastty
8
+ Project-URL: Repository, https://github.com/jmleclercq/hastty
9
+ Project-URL: Issues, https://github.com/jmleclercq/hastty/issues
10
+ Project-URL: Changelog, https://github.com/jmleclercq/hastty/releases
11
+ Keywords: home-assistant,homeassistant,tui,terminal,lovelace,dashboard,textual,smart-home
12
+ Classifier: Development Status :: 3 - Alpha
13
+ Classifier: Environment :: Console
14
+ Classifier: Intended Audience :: End Users/Desktop
15
+ Classifier: License :: OSI Approved :: MIT License
16
+ Classifier: Operating System :: OS Independent
17
+ Classifier: Programming Language :: Python :: 3
18
+ Classifier: Programming Language :: Python :: 3.10
19
+ Classifier: Programming Language :: Python :: 3.11
20
+ Classifier: Programming Language :: Python :: 3.12
21
+ Classifier: Programming Language :: Python :: 3.13
22
+ Classifier: Topic :: Home Automation
23
+ Classifier: Topic :: Terminals
24
+ Requires-Python: >=3.10
25
+ Description-Content-Type: text/markdown
26
+ License-File: LICENSE
27
+ Requires-Dist: textual>=0.58.0
28
+ Requires-Dist: aiohttp>=3.9
29
+ Requires-Dist: PyYAML>=6.0
30
+ Requires-Dist: python-dotenv>=1.0
31
+ Dynamic: license-file
32
+
33
+ # hastty
34
+
35
+ [![Version](https://img.shields.io/badge/version-0.1.1-blue)](https://github.com/jmleclercq/hastty/releases/tag/v0.1.1)
36
+ [![License: MIT](https://img.shields.io/github/license/jmleclercq/hastty)](LICENSE)
37
+
38
+ **A terminal (TUI) viewer for your Home Assistant dashboards — with keyboard
39
+ shortcuts to trigger commands, no browser required.**
40
+
41
+ `hastty` mirrors your existing Lovelace views (same views, same entities, same
42
+ order) and lets you fire commands with the keyboard: toggle a light, run a
43
+ scene, trigger a script or an automation — all from the terminal, with live
44
+ state updates.
45
+
46
+ <p align="center">
47
+ <img src="assets/screenshots/01-view-living-room.svg" width="820" alt="hastty showing the Living Room view, mirrored from Lovelace">
48
+ </p>
49
+
50
+ ## Features
51
+
52
+ - **Mirrors your Lovelace dashboards** — reuses the views and entities you
53
+ already defined in Home Assistant, no separate dashboard to maintain.
54
+ - **Live state updates** — subscribes to `state_changed` events, no polling,
55
+ no manual refresh needed.
56
+ - **Keyboard-first** — jump between views, move the selection, and fire
57
+ commands, all without touching the mouse.
58
+ - **Custom global shortcuts** — bind any key to a specific service call
59
+ (e.g. a scene or script) directly from a config file.
60
+ - **Single dependency on your side**: a Home Assistant Long-Lived Access
61
+ Token. No add-on, no extra integration to install on the HA side.
62
+
63
+ ## Screenshots
64
+
65
+ **Activating an entity** — toggling `switch.tv` with <kbd>Enter</kbd>, state updates live:
66
+
67
+ <img src="assets/screenshots/02-activate-entity.svg" width="820" alt="Activating an entity: toggling switch.tv with Enter, live state update">
68
+
69
+ **Scenes view** — a second Lovelace dashboard, switched to with the <kbd>2</kbd> key:
70
+
71
+ <img src="assets/screenshots/03-view-scenes.svg" width="820" alt="Scenes view, reached with the 2 key">
72
+
73
+ **Help screen** — generic bindings plus the custom shortcuts from `config.yaml`:
74
+
75
+ <img src="assets/screenshots/04-help.svg" width="820" alt="Help screen listing generic bindings and custom shortcuts">
76
+
77
+ *(Captured against a local mock Home Assistant server, see
78
+ [`tests/mock_ha_server.py`](tests/mock_ha_server.py) — no real HA instance
79
+ required to try it out.)*
80
+
81
+ ## Installation
82
+
83
+ ```bash
84
+ git clone https://github.com/jmleclercq/hastty.git
85
+ cd hastty
86
+ python3 -m venv .venv
87
+ source .venv/bin/activate
88
+ pip install -e .
89
+ ```
90
+
91
+ ## Configuration
92
+
93
+ You need two things: the URL of your Home Assistant instance, and a
94
+ **Long-Lived Access Token** (Home Assistant → your profile, bottom left →
95
+ Security tab → "Long-lived access tokens" → Create Token).
96
+
97
+ The simplest option — environment variables:
98
+
99
+ ```bash
100
+ cp .env.example .env
101
+ # edit .env: HA_URL=http://homeassistant.local:8123 and HA_TOKEN=...
102
+ ```
103
+
104
+ Or generate a config file with custom keyboard shortcuts:
105
+
106
+ ```bash
107
+ hastty --init-config
108
+ # edit ~/.config/hastty/config.yaml
109
+ ```
110
+
111
+ The token can stay in `.env` (recommended, never committed) even if you use
112
+ the YAML file for shortcuts.
113
+
114
+ By default, only your **default** Lovelace dashboard is mirrored. If you use
115
+ several dashboards and want all of them, set `include_extra_dashboards: true`
116
+ under `homeassistant:` in `config.yaml`.
117
+
118
+ ## Run
119
+
120
+ ```bash
121
+ hastty
122
+ ```
123
+
124
+ ## Usage
125
+
126
+ | Key | Action |
127
+ |---|---|
128
+ | `1`–`9` | Jump directly to view N (in the order of your HA dashboards) |
129
+ | `←` / `→` | Previous / next view |
130
+ | `↑` / `↓` | Move the selection in the table |
131
+ | `Enter` / `Space` | Activate the selected entity (toggle light/switch, run a scene/script, trigger an automation...) |
132
+ | `r` | Refresh (re-syncs views and states) |
133
+ | `?` | Help (also lists your custom shortcuts) |
134
+ | `q` | Quit |
135
+
136
+ ### Custom shortcuts
137
+
138
+ In `config.yaml`, under `keybindings`, you can define global keys (active
139
+ from any view) that call a service directly — for example a scene or a
140
+ script — without navigating to the entity first:
141
+
142
+ ```yaml
143
+ keybindings:
144
+ - key: "g"
145
+ description: "Good night (scene)"
146
+ service: "scene.turn_on"
147
+ target:
148
+ entity_id: scene.good_night
149
+
150
+ - key: "a"
151
+ description: "Turn everything off (script)"
152
+ service: "script.turn_on"
153
+ target:
154
+ entity_id: script.turn_everything_off
155
+ ```
156
+
157
+ ## How it works
158
+
159
+ - **Connection**: Home Assistant WebSocket API (`/api/websocket`),
160
+ authenticated with a Long-Lived Access Token (`hastty/client.py`).
161
+ - **Dashboard mirroring**: calls `lovelace/config` for your default
162
+ dashboard (and, if `include_extra_dashboards` is enabled, every dashboard
163
+ from `lovelace/dashboards/list` too). Cards (`entities`, `glance`, `light`,
164
+ `vertical-stack`, `grid`, etc.) — whether under a classic top-level
165
+ `cards` list or under the newer per-section `sections` layout used by the
166
+ default dashboard editor since Home Assistant 2024.9 — are flattened into
167
+ per-view lists of `entity_id` (`hastty/lovelace.py`). The app doesn't try
168
+ to reproduce the visual layout of the cards, only the view → entities
169
+ organization.
170
+ - **Commands**: `call_service` over WebSocket, either through the generic
171
+ per-domain action (Enter/Space on a selected entity) or through custom
172
+ shortcuts (`keybindings` in the config).
173
+
174
+ ## Known limitations
175
+
176
+ - `area` cards (without an explicit entity list) aren't resolved into
177
+ individual entities yet.
178
+ - The generic action (Enter/Space) calls a default service based on the
179
+ entity's domain (toggle for light/switch/fan..., turn_on for
180
+ scene/script, trigger for automation...); for a precise command with
181
+ parameters (color, brightness, cover position...), use a custom shortcut
182
+ with `service_data`.
183
+
184
+ ## Development
185
+
186
+ ```bash
187
+ pip install -e .
188
+ python tests/test_integration.py # end-to-end test against a mock HA server
189
+ python tests/capture_screenshots.py # regenerate the SVG screenshots in assets/screenshots
190
+ ```
191
+
192
+ See [CONTRIBUTING.md](CONTRIBUTING.md) for guidelines before opening a PR.
193
+
194
+ ## License
195
+
196
+ [MIT](LICENSE)
hastty-0.1.1/README.md ADDED
@@ -0,0 +1,164 @@
1
+ # hastty
2
+
3
+ [![Version](https://img.shields.io/badge/version-0.1.1-blue)](https://github.com/jmleclercq/hastty/releases/tag/v0.1.1)
4
+ [![License: MIT](https://img.shields.io/github/license/jmleclercq/hastty)](LICENSE)
5
+
6
+ **A terminal (TUI) viewer for your Home Assistant dashboards — with keyboard
7
+ shortcuts to trigger commands, no browser required.**
8
+
9
+ `hastty` mirrors your existing Lovelace views (same views, same entities, same
10
+ order) and lets you fire commands with the keyboard: toggle a light, run a
11
+ scene, trigger a script or an automation — all from the terminal, with live
12
+ state updates.
13
+
14
+ <p align="center">
15
+ <img src="assets/screenshots/01-view-living-room.svg" width="820" alt="hastty showing the Living Room view, mirrored from Lovelace">
16
+ </p>
17
+
18
+ ## Features
19
+
20
+ - **Mirrors your Lovelace dashboards** — reuses the views and entities you
21
+ already defined in Home Assistant, no separate dashboard to maintain.
22
+ - **Live state updates** — subscribes to `state_changed` events, no polling,
23
+ no manual refresh needed.
24
+ - **Keyboard-first** — jump between views, move the selection, and fire
25
+ commands, all without touching the mouse.
26
+ - **Custom global shortcuts** — bind any key to a specific service call
27
+ (e.g. a scene or script) directly from a config file.
28
+ - **Single dependency on your side**: a Home Assistant Long-Lived Access
29
+ Token. No add-on, no extra integration to install on the HA side.
30
+
31
+ ## Screenshots
32
+
33
+ **Activating an entity** — toggling `switch.tv` with <kbd>Enter</kbd>, state updates live:
34
+
35
+ <img src="assets/screenshots/02-activate-entity.svg" width="820" alt="Activating an entity: toggling switch.tv with Enter, live state update">
36
+
37
+ **Scenes view** — a second Lovelace dashboard, switched to with the <kbd>2</kbd> key:
38
+
39
+ <img src="assets/screenshots/03-view-scenes.svg" width="820" alt="Scenes view, reached with the 2 key">
40
+
41
+ **Help screen** — generic bindings plus the custom shortcuts from `config.yaml`:
42
+
43
+ <img src="assets/screenshots/04-help.svg" width="820" alt="Help screen listing generic bindings and custom shortcuts">
44
+
45
+ *(Captured against a local mock Home Assistant server, see
46
+ [`tests/mock_ha_server.py`](tests/mock_ha_server.py) — no real HA instance
47
+ required to try it out.)*
48
+
49
+ ## Installation
50
+
51
+ ```bash
52
+ git clone https://github.com/jmleclercq/hastty.git
53
+ cd hastty
54
+ python3 -m venv .venv
55
+ source .venv/bin/activate
56
+ pip install -e .
57
+ ```
58
+
59
+ ## Configuration
60
+
61
+ You need two things: the URL of your Home Assistant instance, and a
62
+ **Long-Lived Access Token** (Home Assistant → your profile, bottom left →
63
+ Security tab → "Long-lived access tokens" → Create Token).
64
+
65
+ The simplest option — environment variables:
66
+
67
+ ```bash
68
+ cp .env.example .env
69
+ # edit .env: HA_URL=http://homeassistant.local:8123 and HA_TOKEN=...
70
+ ```
71
+
72
+ Or generate a config file with custom keyboard shortcuts:
73
+
74
+ ```bash
75
+ hastty --init-config
76
+ # edit ~/.config/hastty/config.yaml
77
+ ```
78
+
79
+ The token can stay in `.env` (recommended, never committed) even if you use
80
+ the YAML file for shortcuts.
81
+
82
+ By default, only your **default** Lovelace dashboard is mirrored. If you use
83
+ several dashboards and want all of them, set `include_extra_dashboards: true`
84
+ under `homeassistant:` in `config.yaml`.
85
+
86
+ ## Run
87
+
88
+ ```bash
89
+ hastty
90
+ ```
91
+
92
+ ## Usage
93
+
94
+ | Key | Action |
95
+ |---|---|
96
+ | `1`–`9` | Jump directly to view N (in the order of your HA dashboards) |
97
+ | `←` / `→` | Previous / next view |
98
+ | `↑` / `↓` | Move the selection in the table |
99
+ | `Enter` / `Space` | Activate the selected entity (toggle light/switch, run a scene/script, trigger an automation...) |
100
+ | `r` | Refresh (re-syncs views and states) |
101
+ | `?` | Help (also lists your custom shortcuts) |
102
+ | `q` | Quit |
103
+
104
+ ### Custom shortcuts
105
+
106
+ In `config.yaml`, under `keybindings`, you can define global keys (active
107
+ from any view) that call a service directly — for example a scene or a
108
+ script — without navigating to the entity first:
109
+
110
+ ```yaml
111
+ keybindings:
112
+ - key: "g"
113
+ description: "Good night (scene)"
114
+ service: "scene.turn_on"
115
+ target:
116
+ entity_id: scene.good_night
117
+
118
+ - key: "a"
119
+ description: "Turn everything off (script)"
120
+ service: "script.turn_on"
121
+ target:
122
+ entity_id: script.turn_everything_off
123
+ ```
124
+
125
+ ## How it works
126
+
127
+ - **Connection**: Home Assistant WebSocket API (`/api/websocket`),
128
+ authenticated with a Long-Lived Access Token (`hastty/client.py`).
129
+ - **Dashboard mirroring**: calls `lovelace/config` for your default
130
+ dashboard (and, if `include_extra_dashboards` is enabled, every dashboard
131
+ from `lovelace/dashboards/list` too). Cards (`entities`, `glance`, `light`,
132
+ `vertical-stack`, `grid`, etc.) — whether under a classic top-level
133
+ `cards` list or under the newer per-section `sections` layout used by the
134
+ default dashboard editor since Home Assistant 2024.9 — are flattened into
135
+ per-view lists of `entity_id` (`hastty/lovelace.py`). The app doesn't try
136
+ to reproduce the visual layout of the cards, only the view → entities
137
+ organization.
138
+ - **Commands**: `call_service` over WebSocket, either through the generic
139
+ per-domain action (Enter/Space on a selected entity) or through custom
140
+ shortcuts (`keybindings` in the config).
141
+
142
+ ## Known limitations
143
+
144
+ - `area` cards (without an explicit entity list) aren't resolved into
145
+ individual entities yet.
146
+ - The generic action (Enter/Space) calls a default service based on the
147
+ entity's domain (toggle for light/switch/fan..., turn_on for
148
+ scene/script, trigger for automation...); for a precise command with
149
+ parameters (color, brightness, cover position...), use a custom shortcut
150
+ with `service_data`.
151
+
152
+ ## Development
153
+
154
+ ```bash
155
+ pip install -e .
156
+ python tests/test_integration.py # end-to-end test against a mock HA server
157
+ python tests/capture_screenshots.py # regenerate the SVG screenshots in assets/screenshots
158
+ ```
159
+
160
+ See [CONTRIBUTING.md](CONTRIBUTING.md) for guidelines before opening a PR.
161
+
162
+ ## License
163
+
164
+ [MIT](LICENSE)
@@ -0,0 +1,3 @@
1
+ """Home Assistant dashboard viewer for the terminal."""
2
+
3
+ __version__ = "0.1.1"
@@ -0,0 +1,51 @@
1
+ """CLI entry point: `hastty` or `python -m hastty`."""
2
+
3
+ from __future__ import annotations
4
+
5
+ import argparse
6
+ import logging
7
+ import sys
8
+ from pathlib import Path
9
+
10
+ from .app import HasttyApp
11
+ from .config import DEFAULT_CONFIG_PATH, load_config, write_example_config
12
+
13
+
14
+ def run() -> None:
15
+ parser = argparse.ArgumentParser(prog="hastty", description=__doc__)
16
+ parser.add_argument(
17
+ "--config",
18
+ type=Path,
19
+ default=None,
20
+ help=f"Path to the config file (default: {DEFAULT_CONFIG_PATH})",
21
+ )
22
+ parser.add_argument(
23
+ "--init-config",
24
+ action="store_true",
25
+ help="Generate an example config file and exit.",
26
+ )
27
+ parser.add_argument("--debug", action="store_true", help="Enable debug logging to hastty.log")
28
+ args = parser.parse_args()
29
+
30
+ if args.debug:
31
+ logging.basicConfig(filename="hastty.log", level=logging.DEBUG)
32
+
33
+ if args.init_config:
34
+ path = write_example_config(args.config)
35
+ print(f"Example config created: {path}")
36
+ print("Edit this file (or set HA_URL / HA_TOKEN) then run `hastty` again.")
37
+ return
38
+
39
+ try:
40
+ config = load_config(args.config)
41
+ except RuntimeError as exc:
42
+ print(f"Configuration error: {exc}", file=sys.stderr)
43
+ print("\nTip: run `hastty --init-config` to generate a starter config file.", file=sys.stderr)
44
+ sys.exit(1)
45
+
46
+ app = HasttyApp(config)
47
+ app.run()
48
+
49
+
50
+ if __name__ == "__main__":
51
+ run()