entr-ble-cli 1.0.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.
- entr_ble_cli-1.0.0/LICENSE +21 -0
- entr_ble_cli-1.0.0/PKG-INFO +203 -0
- entr_ble_cli-1.0.0/README.md +191 -0
- entr_ble_cli-1.0.0/pyproject.toml +23 -0
- entr_ble_cli-1.0.0/pyproject.toml.orig +25 -0
- entr_ble_cli-1.0.0/src/entr_ble_cli/__init__.py +7 -0
- entr_ble_cli-1.0.0/src/entr_ble_cli/cli.py +132 -0
- entr_ble_cli-1.0.0/src/entr_ble_cli/commands/__init__.py +0 -0
- entr_ble_cli-1.0.0/src/entr_ble_cli/commands/access.py +16 -0
- entr_ble_cli-1.0.0/src/entr_ble_cli/commands/common.py +78 -0
- entr_ble_cli-1.0.0/src/entr_ble_cli/commands/config.py +28 -0
- entr_ble_cli-1.0.0/src/entr_ble_cli/commands/discovery.py +43 -0
- entr_ble_cli-1.0.0/src/entr_ble_cli/commands/maintenance.py +112 -0
- entr_ble_cli-1.0.0/src/entr_ble_cli/commands/settings.py +103 -0
- entr_ble_cli-1.0.0/src/entr_ble_cli/commands/setup.py +110 -0
- entr_ble_cli-1.0.0/src/entr_ble_cli/commands/status.py +38 -0
- entr_ble_cli-1.0.0/src/entr_ble_cli/commands/users.py +120 -0
- entr_ble_cli-1.0.0/src/entr_ble_cli/shared.py +76 -0
- entr_ble_cli-1.0.0/src/entr_ble_cli/store.py +96 -0
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 Johann Lejeune
|
|
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.
|
|
@@ -0,0 +1,203 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: entr-ble-cli
|
|
3
|
+
Version: 1.0.0
|
|
4
|
+
Summary: Command-line interface for ENTR Bluetooth locks
|
|
5
|
+
License-Expression: MIT
|
|
6
|
+
License-File: LICENSE
|
|
7
|
+
Requires-Dist: entr-ble==1.0.0
|
|
8
|
+
Requires-Python: >=3.14
|
|
9
|
+
Project-URL: Repository, https://github.com/johannlejeune/entr-ble
|
|
10
|
+
Project-URL: Issues, https://github.com/johannlejeune/entr-ble/issues
|
|
11
|
+
Description-Content-Type: text/markdown
|
|
12
|
+
|
|
13
|
+
# ENTR BLE · CLI
|
|
14
|
+
|
|
15
|
+
**Set up and control your ENTR lock from the terminal.** Discover locks, manage user keys, change settings, and export credentials to Home Assistant.
|
|
16
|
+
|
|
17
|
+
[Project overview](../../README.md) · [Python library](../../src/entr_ble/README.md) · [Home Assistant](../../custom_components/entr_ble/README.md) · [Feature coverage](../../docs/FEATURES.md)
|
|
18
|
+
|
|
19
|
+
## Install
|
|
20
|
+
|
|
21
|
+
Requires **Python 3.14+** and a working Bluetooth adapter. On Linux, Bluetooth access requires BlueZ and access to the system D-Bus.
|
|
22
|
+
|
|
23
|
+
Run the CLI without a checkout:
|
|
24
|
+
|
|
25
|
+
```sh
|
|
26
|
+
uvx entr-ble-cli --help
|
|
27
|
+
uvx entr-ble-cli scan
|
|
28
|
+
```
|
|
29
|
+
|
|
30
|
+
For a persistent command, use `uv tool install entr-ble-cli`, then run `entr-ble-cli` directly. With `uvx`, prefix the commands below with `uvx`.
|
|
31
|
+
|
|
32
|
+
Alternatively, from a checkout, run:
|
|
33
|
+
|
|
34
|
+
```sh
|
|
35
|
+
uv sync --locked --package entr-ble-cli
|
|
36
|
+
uv run --package entr-ble-cli entr-ble-cli --help
|
|
37
|
+
```
|
|
38
|
+
|
|
39
|
+
For the checkout workflow, prefix the commands below with `uv run --package entr-ble-cli`. The `entr-ble-cli` package supplies the `entr-ble-cli` command; installing the `entr-ble` library alone does not.
|
|
40
|
+
|
|
41
|
+
## Quick start
|
|
42
|
+
|
|
43
|
+
### 1. Find your lock
|
|
44
|
+
|
|
45
|
+
```sh
|
|
46
|
+
entr-ble-cli scan
|
|
47
|
+
```
|
|
48
|
+
|
|
49
|
+
The scan lists each lock's address, advertised name, initialization state, and signal strength. Replace `ADDRESS` in the examples below with the exact address shown by your scan.
|
|
50
|
+
|
|
51
|
+
For a longer scan, or to include other BLE devices:
|
|
52
|
+
|
|
53
|
+
```sh
|
|
54
|
+
entr-ble-cli scan --timeout 10
|
|
55
|
+
entr-ble-cli scan --all
|
|
56
|
+
```
|
|
57
|
+
|
|
58
|
+
### 2. Choose how to access the lock
|
|
59
|
+
|
|
60
|
+
Run **one** of the following setup commands. Each saves the resulting credential locally for subsequent commands.
|
|
61
|
+
|
|
62
|
+
| Your situation | Command | Effect |
|
|
63
|
+
| --- | --- | --- |
|
|
64
|
+
| New, uninitialized lock | `entr-ble-cli set-owner ADDRESS --name "Front door"` | Creates the first owner and asks for a new password |
|
|
65
|
+
| Initialized lock; you want owner access | `entr-ble-cli enroll ADDRESS --name "Front door"` | Asks for the current owner password and replaces the owner credential |
|
|
66
|
+
| You have an activation code from the owner | `entr-ble-cli activate ADDRESS KEY_CODE` | Redeems an additional key while preserving the owner |
|
|
67
|
+
|
|
68
|
+
> [!WARNING]
|
|
69
|
+
> `enroll` replaces the lock's single owner slot. The previous owner, including the mobile app if it holds that slot, loses access. Use `activate` to keep the existing owner.
|
|
70
|
+
|
|
71
|
+
`KEY_CODE` is the six-character code supplied by the owner. A setup command refuses to overwrite credentials already saved for that address.
|
|
72
|
+
|
|
73
|
+
For a new lock, `--name` accepts up to 12 bytes. `--user` sets the owner name, and `--provider` selects the brand id; see `entr-ble-cli set-owner --help`. After initial setup, an uncalibrated lock needs `calibrate` followed by `magnet-calibrate` with the door magnet in place.
|
|
74
|
+
|
|
75
|
+
### 3. Read status and control the lock
|
|
76
|
+
|
|
77
|
+
```sh
|
|
78
|
+
entr-ble-cli status ADDRESS
|
|
79
|
+
entr-ble-cli unlock ADDRESS
|
|
80
|
+
entr-ble-cli lock ADDRESS
|
|
81
|
+
```
|
|
82
|
+
|
|
83
|
+
Commands restore your saved session and close the Bluetooth connection when finished. Unlock behavior follows the lock's auto-lock setting. Reported state can be stale after manual operation; see [protocol limitations](../../docs/PROTOCOL.md).
|
|
84
|
+
|
|
85
|
+
## Everyday tasks
|
|
86
|
+
|
|
87
|
+
### Share access
|
|
88
|
+
|
|
89
|
+
With an owner or admin key, create a user:
|
|
90
|
+
|
|
91
|
+
```sh
|
|
92
|
+
entr-ble-cli create-user ADDRESS Guest
|
|
93
|
+
```
|
|
94
|
+
|
|
95
|
+
The command asks for the admin password and prints an activation code and the command to redeem it. Run that activation command on the recipient's machine with its own credential store.
|
|
96
|
+
|
|
97
|
+
The code stays redeemable for **three hours by default**. `--expiration` changes that window; the activated key does not expire when the code's redemption window ends.
|
|
98
|
+
|
|
99
|
+
```sh
|
|
100
|
+
entr-ble-cli list-users ADDRESS
|
|
101
|
+
entr-ble-cli disable-user ADDRESS Guest
|
|
102
|
+
entr-ble-cli enable-user ADDRESS Guest
|
|
103
|
+
entr-ble-cli delete-user ADDRESS Guest
|
|
104
|
+
```
|
|
105
|
+
|
|
106
|
+
Disable suspends a key; enable restores it; delete permanently revokes it. `create-user --role admin` creates an admin key. After activation, that admin uses `set-admin-code` to set its personal password. Accessory roles are also available; radio pairing happens on the hardware, as described in [feature coverage](../../docs/FEATURES.md#users-and-accessories).
|
|
107
|
+
|
|
108
|
+
### Change settings
|
|
109
|
+
|
|
110
|
+
```sh
|
|
111
|
+
entr-ble-cli settings ADDRESS --volume medium --auto-lock on
|
|
112
|
+
entr-ble-cli change-admin-code ADDRESS
|
|
113
|
+
```
|
|
114
|
+
|
|
115
|
+
Volume choices are `high`, `medium`, `low`, and `muted`. These commands require an owner or admin key. The CLI reads the current configuration before applying changes. If the lock name was not saved during enrollment, pass `--name "Front door"` once; the CLI saves it for later settings commands.
|
|
116
|
+
|
|
117
|
+
### Bring an existing key into Home Assistant
|
|
118
|
+
|
|
119
|
+
```sh
|
|
120
|
+
entr-ble-cli export-homeassistant ADDRESS
|
|
121
|
+
```
|
|
122
|
+
|
|
123
|
+
In the [integration setup](../../custom_components/entr_ble/README.md#use-credentials-from-the-cli), choose **Import existing credentials**, enter the same address, and paste the output into **Credentials JSON**.
|
|
124
|
+
|
|
125
|
+
Export reads the local store without connecting to the lock or changing credentials. Home Assistant stores its own copy. The JSON contains access keys: keep it private.
|
|
126
|
+
|
|
127
|
+
## Command reference
|
|
128
|
+
|
|
129
|
+
Run `entr-ble-cli` to see grouped help, or `entr-ble-cli COMMAND --help` for arguments and options. All lock commands below take `ADDRESS`; `scan` does not.
|
|
130
|
+
|
|
131
|
+
| Group | Commands | Purpose |
|
|
132
|
+
| --- | --- | --- |
|
|
133
|
+
| Discovery and setup | `scan`, `set-owner`, `enroll`, `activate` | Find locks and save an access credential |
|
|
134
|
+
| Lock control | `lock`, `unlock` | Operate the lock |
|
|
135
|
+
| Status and information | `status`, `info`, `device-info` | Read status, serial number, model, and firmware |
|
|
136
|
+
| Users and keys | `list-users`, `create-user`, `set-admin-code`, `disable-user`, `enable-user`, `delete-user` | Manage access |
|
|
137
|
+
| Settings | `settings`, `change-admin-code` | Change volume, auto-lock, name, or admin password |
|
|
138
|
+
| Maintenance | `calibrate`, `magnet-calibrate`, `factory-reset` | Calibrate hardware or restore factory settings |
|
|
139
|
+
| Clock and logs | `set-time`, `audit-trail`, `get-errors` | Set UTC time and read diagnostics |
|
|
140
|
+
| Home Assistant | `export-homeassistant` | Export one saved credential as JSON |
|
|
141
|
+
|
|
142
|
+
`set-time` and `audit-trail` require NIZ firmware. `device-info` requires the FOTA GATT service and communication version 1.29r3+; `get-errors` requires the FOTA service. See [feature coverage](../../docs/FEATURES.md) for the full compatibility matrix.
|
|
143
|
+
|
|
144
|
+
> [!WARNING]
|
|
145
|
+
> `factory-reset` erases lock users and settings, then removes the local credential after success. It asks you to type `yes`; `--yes` skips that confirmation.
|
|
146
|
+
|
|
147
|
+
## Passwords and scripting
|
|
148
|
+
|
|
149
|
+
Commands needing a password ask for it **without echo**, before connecting. `set-owner` and `set-admin-code` ask for the password to set; `change-admin-code` asks for both current and new passwords. At the `audit-trail` prompt, press Enter to use the factory audit password.
|
|
150
|
+
|
|
151
|
+
For scripts without a terminal, use `-p PASSWORD` or `--password PASSWORD`. For password changes, also supply `--new-password NEW`. Command-line passwords may appear in shell history and process arguments; prefer the hidden prompt when working interactively.
|
|
152
|
+
|
|
153
|
+
Results go to **stdout**, progress to **stderr**. Add `-q` or `--quiet` before or after a command to suppress progress while keeping warnings and errors:
|
|
154
|
+
|
|
155
|
+
```sh
|
|
156
|
+
entr-ble-cli --quiet status ADDRESS > status.txt
|
|
157
|
+
entr-ble-cli status ADDRESS --quiet
|
|
158
|
+
```
|
|
159
|
+
|
|
160
|
+
Expected Bluetooth and lock failures produce a short error and exit with code `1`; interrupted input exits with `130`. Unexpected internal errors retain their traceback. Ordinary command output is human-readable; `export-homeassistant` produces JSON.
|
|
161
|
+
|
|
162
|
+
## Credential storage
|
|
163
|
+
|
|
164
|
+
| Setting | Value |
|
|
165
|
+
| --- | --- |
|
|
166
|
+
| Default store | `~/.config/entr-ble/credentials.json` |
|
|
167
|
+
| Override | `ENTR_BLE_STORE` environment variable |
|
|
168
|
+
| Protection | Access restricted to the current user; unencrypted at rest |
|
|
169
|
+
| Writes | Atomic file replacement; no coordination between concurrent writers |
|
|
170
|
+
|
|
171
|
+
Keep the store private, backed up securely, and out of version control. Use one process at a time when modifying a shared store. An alternate store can keep credentials for a separate client:
|
|
172
|
+
|
|
173
|
+
```sh
|
|
174
|
+
export ENTR_BLE_STORE="$HOME/.config/entr-ble/guest.json"
|
|
175
|
+
entr-ble-cli activate ADDRESS KEY_CODE
|
|
176
|
+
```
|
|
177
|
+
|
|
178
|
+
Subsequent commands in that shell use the alternate store.
|
|
179
|
+
|
|
180
|
+
## Troubleshooting
|
|
181
|
+
|
|
182
|
+
| Symptom | Next step |
|
|
183
|
+
| --- | --- |
|
|
184
|
+
| No lock found or communication times out | Move closer, check Bluetooth is enabled, and try `scan --timeout 10 --all` |
|
|
185
|
+
| Bluetooth access denied | Check application Bluetooth permissions; on Linux, check BlueZ and system D-Bus access |
|
|
186
|
+
| No saved key | Check the address and `ENTR_BLE_STORE`; choose the appropriate setup method above |
|
|
187
|
+
| Lock refuses a command | Check the password, key permissions, and firmware support |
|
|
188
|
+
| Settings command needs a name | Pass `--name` once to save the lock name locally |
|
|
189
|
+
| Status disagrees with manual operation | Read the [firmware state limitations](../../docs/PROTOCOL.md); a reported value can be stale |
|
|
190
|
+
|
|
191
|
+
For a bug report, include the command, error, software version, and lock model/firmware. Remove passwords and credential material before sharing output.
|
|
192
|
+
|
|
193
|
+
## Development
|
|
194
|
+
|
|
195
|
+
From the repository root, after the checkout installation above:
|
|
196
|
+
|
|
197
|
+
```sh
|
|
198
|
+
uv run --package entr-ble-cli python -m unittest discover -s packages/entr-ble-cli/tests -v
|
|
199
|
+
```
|
|
200
|
+
|
|
201
|
+
Tests use simulated BLE responses and do not operate a lock. See the [workspace development guide](../../README.md#development) for all checks.
|
|
202
|
+
|
|
203
|
+
Licensed under the [MIT License](LICENSE).
|
|
@@ -0,0 +1,191 @@
|
|
|
1
|
+
# ENTR BLE · CLI
|
|
2
|
+
|
|
3
|
+
**Set up and control your ENTR lock from the terminal.** Discover locks, manage user keys, change settings, and export credentials to Home Assistant.
|
|
4
|
+
|
|
5
|
+
[Project overview](../../README.md) · [Python library](../../src/entr_ble/README.md) · [Home Assistant](../../custom_components/entr_ble/README.md) · [Feature coverage](../../docs/FEATURES.md)
|
|
6
|
+
|
|
7
|
+
## Install
|
|
8
|
+
|
|
9
|
+
Requires **Python 3.14+** and a working Bluetooth adapter. On Linux, Bluetooth access requires BlueZ and access to the system D-Bus.
|
|
10
|
+
|
|
11
|
+
Run the CLI without a checkout:
|
|
12
|
+
|
|
13
|
+
```sh
|
|
14
|
+
uvx entr-ble-cli --help
|
|
15
|
+
uvx entr-ble-cli scan
|
|
16
|
+
```
|
|
17
|
+
|
|
18
|
+
For a persistent command, use `uv tool install entr-ble-cli`, then run `entr-ble-cli` directly. With `uvx`, prefix the commands below with `uvx`.
|
|
19
|
+
|
|
20
|
+
Alternatively, from a checkout, run:
|
|
21
|
+
|
|
22
|
+
```sh
|
|
23
|
+
uv sync --locked --package entr-ble-cli
|
|
24
|
+
uv run --package entr-ble-cli entr-ble-cli --help
|
|
25
|
+
```
|
|
26
|
+
|
|
27
|
+
For the checkout workflow, prefix the commands below with `uv run --package entr-ble-cli`. The `entr-ble-cli` package supplies the `entr-ble-cli` command; installing the `entr-ble` library alone does not.
|
|
28
|
+
|
|
29
|
+
## Quick start
|
|
30
|
+
|
|
31
|
+
### 1. Find your lock
|
|
32
|
+
|
|
33
|
+
```sh
|
|
34
|
+
entr-ble-cli scan
|
|
35
|
+
```
|
|
36
|
+
|
|
37
|
+
The scan lists each lock's address, advertised name, initialization state, and signal strength. Replace `ADDRESS` in the examples below with the exact address shown by your scan.
|
|
38
|
+
|
|
39
|
+
For a longer scan, or to include other BLE devices:
|
|
40
|
+
|
|
41
|
+
```sh
|
|
42
|
+
entr-ble-cli scan --timeout 10
|
|
43
|
+
entr-ble-cli scan --all
|
|
44
|
+
```
|
|
45
|
+
|
|
46
|
+
### 2. Choose how to access the lock
|
|
47
|
+
|
|
48
|
+
Run **one** of the following setup commands. Each saves the resulting credential locally for subsequent commands.
|
|
49
|
+
|
|
50
|
+
| Your situation | Command | Effect |
|
|
51
|
+
| --- | --- | --- |
|
|
52
|
+
| New, uninitialized lock | `entr-ble-cli set-owner ADDRESS --name "Front door"` | Creates the first owner and asks for a new password |
|
|
53
|
+
| Initialized lock; you want owner access | `entr-ble-cli enroll ADDRESS --name "Front door"` | Asks for the current owner password and replaces the owner credential |
|
|
54
|
+
| You have an activation code from the owner | `entr-ble-cli activate ADDRESS KEY_CODE` | Redeems an additional key while preserving the owner |
|
|
55
|
+
|
|
56
|
+
> [!WARNING]
|
|
57
|
+
> `enroll` replaces the lock's single owner slot. The previous owner, including the mobile app if it holds that slot, loses access. Use `activate` to keep the existing owner.
|
|
58
|
+
|
|
59
|
+
`KEY_CODE` is the six-character code supplied by the owner. A setup command refuses to overwrite credentials already saved for that address.
|
|
60
|
+
|
|
61
|
+
For a new lock, `--name` accepts up to 12 bytes. `--user` sets the owner name, and `--provider` selects the brand id; see `entr-ble-cli set-owner --help`. After initial setup, an uncalibrated lock needs `calibrate` followed by `magnet-calibrate` with the door magnet in place.
|
|
62
|
+
|
|
63
|
+
### 3. Read status and control the lock
|
|
64
|
+
|
|
65
|
+
```sh
|
|
66
|
+
entr-ble-cli status ADDRESS
|
|
67
|
+
entr-ble-cli unlock ADDRESS
|
|
68
|
+
entr-ble-cli lock ADDRESS
|
|
69
|
+
```
|
|
70
|
+
|
|
71
|
+
Commands restore your saved session and close the Bluetooth connection when finished. Unlock behavior follows the lock's auto-lock setting. Reported state can be stale after manual operation; see [protocol limitations](../../docs/PROTOCOL.md).
|
|
72
|
+
|
|
73
|
+
## Everyday tasks
|
|
74
|
+
|
|
75
|
+
### Share access
|
|
76
|
+
|
|
77
|
+
With an owner or admin key, create a user:
|
|
78
|
+
|
|
79
|
+
```sh
|
|
80
|
+
entr-ble-cli create-user ADDRESS Guest
|
|
81
|
+
```
|
|
82
|
+
|
|
83
|
+
The command asks for the admin password and prints an activation code and the command to redeem it. Run that activation command on the recipient's machine with its own credential store.
|
|
84
|
+
|
|
85
|
+
The code stays redeemable for **three hours by default**. `--expiration` changes that window; the activated key does not expire when the code's redemption window ends.
|
|
86
|
+
|
|
87
|
+
```sh
|
|
88
|
+
entr-ble-cli list-users ADDRESS
|
|
89
|
+
entr-ble-cli disable-user ADDRESS Guest
|
|
90
|
+
entr-ble-cli enable-user ADDRESS Guest
|
|
91
|
+
entr-ble-cli delete-user ADDRESS Guest
|
|
92
|
+
```
|
|
93
|
+
|
|
94
|
+
Disable suspends a key; enable restores it; delete permanently revokes it. `create-user --role admin` creates an admin key. After activation, that admin uses `set-admin-code` to set its personal password. Accessory roles are also available; radio pairing happens on the hardware, as described in [feature coverage](../../docs/FEATURES.md#users-and-accessories).
|
|
95
|
+
|
|
96
|
+
### Change settings
|
|
97
|
+
|
|
98
|
+
```sh
|
|
99
|
+
entr-ble-cli settings ADDRESS --volume medium --auto-lock on
|
|
100
|
+
entr-ble-cli change-admin-code ADDRESS
|
|
101
|
+
```
|
|
102
|
+
|
|
103
|
+
Volume choices are `high`, `medium`, `low`, and `muted`. These commands require an owner or admin key. The CLI reads the current configuration before applying changes. If the lock name was not saved during enrollment, pass `--name "Front door"` once; the CLI saves it for later settings commands.
|
|
104
|
+
|
|
105
|
+
### Bring an existing key into Home Assistant
|
|
106
|
+
|
|
107
|
+
```sh
|
|
108
|
+
entr-ble-cli export-homeassistant ADDRESS
|
|
109
|
+
```
|
|
110
|
+
|
|
111
|
+
In the [integration setup](../../custom_components/entr_ble/README.md#use-credentials-from-the-cli), choose **Import existing credentials**, enter the same address, and paste the output into **Credentials JSON**.
|
|
112
|
+
|
|
113
|
+
Export reads the local store without connecting to the lock or changing credentials. Home Assistant stores its own copy. The JSON contains access keys: keep it private.
|
|
114
|
+
|
|
115
|
+
## Command reference
|
|
116
|
+
|
|
117
|
+
Run `entr-ble-cli` to see grouped help, or `entr-ble-cli COMMAND --help` for arguments and options. All lock commands below take `ADDRESS`; `scan` does not.
|
|
118
|
+
|
|
119
|
+
| Group | Commands | Purpose |
|
|
120
|
+
| --- | --- | --- |
|
|
121
|
+
| Discovery and setup | `scan`, `set-owner`, `enroll`, `activate` | Find locks and save an access credential |
|
|
122
|
+
| Lock control | `lock`, `unlock` | Operate the lock |
|
|
123
|
+
| Status and information | `status`, `info`, `device-info` | Read status, serial number, model, and firmware |
|
|
124
|
+
| Users and keys | `list-users`, `create-user`, `set-admin-code`, `disable-user`, `enable-user`, `delete-user` | Manage access |
|
|
125
|
+
| Settings | `settings`, `change-admin-code` | Change volume, auto-lock, name, or admin password |
|
|
126
|
+
| Maintenance | `calibrate`, `magnet-calibrate`, `factory-reset` | Calibrate hardware or restore factory settings |
|
|
127
|
+
| Clock and logs | `set-time`, `audit-trail`, `get-errors` | Set UTC time and read diagnostics |
|
|
128
|
+
| Home Assistant | `export-homeassistant` | Export one saved credential as JSON |
|
|
129
|
+
|
|
130
|
+
`set-time` and `audit-trail` require NIZ firmware. `device-info` requires the FOTA GATT service and communication version 1.29r3+; `get-errors` requires the FOTA service. See [feature coverage](../../docs/FEATURES.md) for the full compatibility matrix.
|
|
131
|
+
|
|
132
|
+
> [!WARNING]
|
|
133
|
+
> `factory-reset` erases lock users and settings, then removes the local credential after success. It asks you to type `yes`; `--yes` skips that confirmation.
|
|
134
|
+
|
|
135
|
+
## Passwords and scripting
|
|
136
|
+
|
|
137
|
+
Commands needing a password ask for it **without echo**, before connecting. `set-owner` and `set-admin-code` ask for the password to set; `change-admin-code` asks for both current and new passwords. At the `audit-trail` prompt, press Enter to use the factory audit password.
|
|
138
|
+
|
|
139
|
+
For scripts without a terminal, use `-p PASSWORD` or `--password PASSWORD`. For password changes, also supply `--new-password NEW`. Command-line passwords may appear in shell history and process arguments; prefer the hidden prompt when working interactively.
|
|
140
|
+
|
|
141
|
+
Results go to **stdout**, progress to **stderr**. Add `-q` or `--quiet` before or after a command to suppress progress while keeping warnings and errors:
|
|
142
|
+
|
|
143
|
+
```sh
|
|
144
|
+
entr-ble-cli --quiet status ADDRESS > status.txt
|
|
145
|
+
entr-ble-cli status ADDRESS --quiet
|
|
146
|
+
```
|
|
147
|
+
|
|
148
|
+
Expected Bluetooth and lock failures produce a short error and exit with code `1`; interrupted input exits with `130`. Unexpected internal errors retain their traceback. Ordinary command output is human-readable; `export-homeassistant` produces JSON.
|
|
149
|
+
|
|
150
|
+
## Credential storage
|
|
151
|
+
|
|
152
|
+
| Setting | Value |
|
|
153
|
+
| --- | --- |
|
|
154
|
+
| Default store | `~/.config/entr-ble/credentials.json` |
|
|
155
|
+
| Override | `ENTR_BLE_STORE` environment variable |
|
|
156
|
+
| Protection | Access restricted to the current user; unencrypted at rest |
|
|
157
|
+
| Writes | Atomic file replacement; no coordination between concurrent writers |
|
|
158
|
+
|
|
159
|
+
Keep the store private, backed up securely, and out of version control. Use one process at a time when modifying a shared store. An alternate store can keep credentials for a separate client:
|
|
160
|
+
|
|
161
|
+
```sh
|
|
162
|
+
export ENTR_BLE_STORE="$HOME/.config/entr-ble/guest.json"
|
|
163
|
+
entr-ble-cli activate ADDRESS KEY_CODE
|
|
164
|
+
```
|
|
165
|
+
|
|
166
|
+
Subsequent commands in that shell use the alternate store.
|
|
167
|
+
|
|
168
|
+
## Troubleshooting
|
|
169
|
+
|
|
170
|
+
| Symptom | Next step |
|
|
171
|
+
| --- | --- |
|
|
172
|
+
| No lock found or communication times out | Move closer, check Bluetooth is enabled, and try `scan --timeout 10 --all` |
|
|
173
|
+
| Bluetooth access denied | Check application Bluetooth permissions; on Linux, check BlueZ and system D-Bus access |
|
|
174
|
+
| No saved key | Check the address and `ENTR_BLE_STORE`; choose the appropriate setup method above |
|
|
175
|
+
| Lock refuses a command | Check the password, key permissions, and firmware support |
|
|
176
|
+
| Settings command needs a name | Pass `--name` once to save the lock name locally |
|
|
177
|
+
| Status disagrees with manual operation | Read the [firmware state limitations](../../docs/PROTOCOL.md); a reported value can be stale |
|
|
178
|
+
|
|
179
|
+
For a bug report, include the command, error, software version, and lock model/firmware. Remove passwords and credential material before sharing output.
|
|
180
|
+
|
|
181
|
+
## Development
|
|
182
|
+
|
|
183
|
+
From the repository root, after the checkout installation above:
|
|
184
|
+
|
|
185
|
+
```sh
|
|
186
|
+
uv run --package entr-ble-cli python -m unittest discover -s packages/entr-ble-cli/tests -v
|
|
187
|
+
```
|
|
188
|
+
|
|
189
|
+
Tests use simulated BLE responses and do not operate a lock. See the [workspace development guide](../../README.md#development) for all checks.
|
|
190
|
+
|
|
191
|
+
Licensed under the [MIT License](LICENSE).
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
[project]
|
|
2
|
+
name = "entr-ble-cli"
|
|
3
|
+
version = "1.0.0"
|
|
4
|
+
description = "Command-line interface for ENTR Bluetooth locks"
|
|
5
|
+
readme = "README.md"
|
|
6
|
+
license = "MIT"
|
|
7
|
+
license-files = ["LICENSE"]
|
|
8
|
+
requires-python = ">=3.14"
|
|
9
|
+
dependencies = ["entr-ble==1.0.0"]
|
|
10
|
+
|
|
11
|
+
[project.urls]
|
|
12
|
+
Repository = "https://github.com/johannlejeune/entr-ble"
|
|
13
|
+
Issues = "https://github.com/johannlejeune/entr-ble/issues"
|
|
14
|
+
|
|
15
|
+
[project.scripts]
|
|
16
|
+
entr-ble-cli = "entr_ble_cli:main"
|
|
17
|
+
|
|
18
|
+
[tool.uv.sources.entr-ble]
|
|
19
|
+
workspace = true
|
|
20
|
+
|
|
21
|
+
[build-system]
|
|
22
|
+
requires = ["uv_build>=0.12.0,<0.13.0"]
|
|
23
|
+
build-backend = "uv_build"
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
[project]
|
|
2
|
+
name = "entr-ble-cli"
|
|
3
|
+
version = "1.0.0"
|
|
4
|
+
description = "Command-line interface for ENTR Bluetooth locks"
|
|
5
|
+
readme = "README.md"
|
|
6
|
+
license = "MIT"
|
|
7
|
+
license-files = ["LICENSE"]
|
|
8
|
+
requires-python = ">=3.14"
|
|
9
|
+
dependencies = [
|
|
10
|
+
"entr-ble==1.0.0",
|
|
11
|
+
]
|
|
12
|
+
|
|
13
|
+
[project.urls]
|
|
14
|
+
Repository = "https://github.com/johannlejeune/entr-ble"
|
|
15
|
+
Issues = "https://github.com/johannlejeune/entr-ble/issues"
|
|
16
|
+
|
|
17
|
+
[project.scripts]
|
|
18
|
+
entr-ble-cli = "entr_ble_cli:main"
|
|
19
|
+
|
|
20
|
+
[tool.uv.sources]
|
|
21
|
+
entr-ble = { workspace = true }
|
|
22
|
+
|
|
23
|
+
[build-system]
|
|
24
|
+
requires = ["uv_build>=0.12.0,<0.13.0"]
|
|
25
|
+
build-backend = "uv_build"
|
|
@@ -0,0 +1,132 @@
|
|
|
1
|
+
import argparse
|
|
2
|
+
import asyncio
|
|
3
|
+
import logging
|
|
4
|
+
|
|
5
|
+
from bleak.exc import (
|
|
6
|
+
BleakBluetoothNotAvailableError,
|
|
7
|
+
BleakBluetoothNotAvailableReason,
|
|
8
|
+
BleakDeviceNotFoundError,
|
|
9
|
+
BleakError,
|
|
10
|
+
)
|
|
11
|
+
|
|
12
|
+
from entr_ble import EntrLockError, EntrProtocolError
|
|
13
|
+
|
|
14
|
+
from .commands import (
|
|
15
|
+
access,
|
|
16
|
+
config,
|
|
17
|
+
discovery,
|
|
18
|
+
maintenance,
|
|
19
|
+
settings,
|
|
20
|
+
setup,
|
|
21
|
+
status,
|
|
22
|
+
users,
|
|
23
|
+
)
|
|
24
|
+
from .commands.common import handle
|
|
25
|
+
from .shared import CommandError
|
|
26
|
+
|
|
27
|
+
|
|
28
|
+
class HelpFormatter(argparse.RawDescriptionHelpFormatter):
|
|
29
|
+
def __init__(self, prog, groups):
|
|
30
|
+
super().__init__(prog, max_help_position=32)
|
|
31
|
+
self.groups = groups
|
|
32
|
+
|
|
33
|
+
def _format_action(self, action):
|
|
34
|
+
if not isinstance(action, argparse._SubParsersAction):
|
|
35
|
+
return super()._format_action(action)
|
|
36
|
+
commands = {command.dest: command for command in action._get_subactions()}
|
|
37
|
+
parts = []
|
|
38
|
+
for title, names in self.groups:
|
|
39
|
+
parts.append(f"{' ' * self._current_indent}{title}:\n")
|
|
40
|
+
self._indent()
|
|
41
|
+
for name in names:
|
|
42
|
+
parts.append(super()._format_action(commands[name]))
|
|
43
|
+
self._dedent()
|
|
44
|
+
parts.append("\n")
|
|
45
|
+
return "".join(parts)
|
|
46
|
+
|
|
47
|
+
|
|
48
|
+
def main() -> None:
|
|
49
|
+
groups = []
|
|
50
|
+
parser = argparse.ArgumentParser(
|
|
51
|
+
prog="entr-ble-cli",
|
|
52
|
+
description="Set up and control an ENTR Bluetooth lock.",
|
|
53
|
+
usage="%(prog)s [options] COMMAND ...",
|
|
54
|
+
formatter_class=lambda prog: HelpFormatter(prog, groups),
|
|
55
|
+
epilog=(
|
|
56
|
+
"Getting started:\n"
|
|
57
|
+
" 1. Find your lock: entr-ble-cli scan\n"
|
|
58
|
+
" 2. Set up access with set-owner (new lock), enroll (replace the owner),\n"
|
|
59
|
+
" or activate (use a key supplied by the owner).\n"
|
|
60
|
+
" 3. Read its status: entr-ble-cli status ADDRESS\n\n"
|
|
61
|
+
"Replace ADDRESS with the Bluetooth address printed by scan.\n"
|
|
62
|
+
"For a command's arguments and options, run:\n"
|
|
63
|
+
" entr-ble-cli COMMAND --help\n\n"
|
|
64
|
+
"Example: entr-ble-cli unlock AA:BB:CC:DD:EE:FF"
|
|
65
|
+
),
|
|
66
|
+
)
|
|
67
|
+
parser.add_argument(
|
|
68
|
+
"-q", "--quiet", action="store_true", help="hide progress messages"
|
|
69
|
+
)
|
|
70
|
+
sub = parser.add_subparsers(dest="command", title="commands", metavar="COMMAND")
|
|
71
|
+
for title, modules in (
|
|
72
|
+
("Discovery and setup", (discovery, setup)),
|
|
73
|
+
("Lock control", (access,)),
|
|
74
|
+
("Status and information", (status,)),
|
|
75
|
+
("Users and keys", (users,)),
|
|
76
|
+
("Settings", (settings,)),
|
|
77
|
+
("Maintenance and logs", (maintenance,)),
|
|
78
|
+
("Home Assistant", (config,)),
|
|
79
|
+
):
|
|
80
|
+
existing = set(sub.choices)
|
|
81
|
+
for module in modules:
|
|
82
|
+
module.register(sub)
|
|
83
|
+
groups.append((title, [name for name in sub.choices if name not in existing]))
|
|
84
|
+
for command in sub.choices.values():
|
|
85
|
+
command.add_argument(
|
|
86
|
+
"-q",
|
|
87
|
+
"--quiet",
|
|
88
|
+
action="store_true",
|
|
89
|
+
default=argparse.SUPPRESS,
|
|
90
|
+
help="hide progress messages",
|
|
91
|
+
)
|
|
92
|
+
args = parser.parse_args()
|
|
93
|
+
if args.command is None:
|
|
94
|
+
parser.print_help()
|
|
95
|
+
return
|
|
96
|
+
logger = logging.getLogger("entr_ble_cli")
|
|
97
|
+
logger.handlers = [logging.StreamHandler()]
|
|
98
|
+
logger.setLevel(logging.WARNING if args.quiet else logging.INFO)
|
|
99
|
+
logger.propagate = False
|
|
100
|
+
try:
|
|
101
|
+
if args.command == "export-homeassistant":
|
|
102
|
+
config.run(args)
|
|
103
|
+
else:
|
|
104
|
+
asyncio.run(handle(args))
|
|
105
|
+
except (CommandError, EntrProtocolError, BleakError, OSError, ValueError) as exc:
|
|
106
|
+
parser.exit(1, f"Error: {error_message(exc)}\n")
|
|
107
|
+
except KeyboardInterrupt, EOFError:
|
|
108
|
+
parser.exit(130, "Aborted.\n")
|
|
109
|
+
|
|
110
|
+
|
|
111
|
+
def error_message(exc):
|
|
112
|
+
if isinstance(exc, BleakBluetoothNotAvailableError):
|
|
113
|
+
if exc.reason == BleakBluetoothNotAvailableReason.POWERED_OFF:
|
|
114
|
+
return "Bluetooth is turned off. Turn it on and try again."
|
|
115
|
+
if exc.reason.name.startswith("DENIED_"):
|
|
116
|
+
return "Bluetooth access was denied. Allow this application to use Bluetooth and try again."
|
|
117
|
+
return "Bluetooth is unavailable. Check that a Bluetooth adapter is connected and enabled."
|
|
118
|
+
if isinstance(exc, BleakDeviceNotFoundError):
|
|
119
|
+
return "The lock could not be found. Move closer and run 'entr-ble-cli scan' to check its address."
|
|
120
|
+
if isinstance(exc, TimeoutError):
|
|
121
|
+
return "Bluetooth communication timed out. Move closer to the lock and check that Bluetooth is enabled."
|
|
122
|
+
if isinstance(exc, BleakError):
|
|
123
|
+
return "Bluetooth communication failed. Check that Bluetooth is enabled and the lock is nearby, then try again."
|
|
124
|
+
if isinstance(exc, EntrLockError):
|
|
125
|
+
return "The lock refused the command. Check your password, key permissions, and whether your lock supports this command."
|
|
126
|
+
if isinstance(exc, EntrProtocolError):
|
|
127
|
+
return "The lock sent an unexpected response. Check the connection and whether your lock supports this command."
|
|
128
|
+
if isinstance(exc, OSError):
|
|
129
|
+
if exc.filename:
|
|
130
|
+
return f"Could not access {exc.filename}: {exc.strerror or 'check the file and its permissions'}."
|
|
131
|
+
return "Could not communicate over Bluetooth. Check your adapter and Bluetooth service, then try again."
|
|
132
|
+
return str(exc)
|
|
File without changes
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
def register(sub):
|
|
2
|
+
p = sub.add_parser("unlock", help="unlock the door")
|
|
3
|
+
p.add_argument("address")
|
|
4
|
+
p.set_defaults(run=run)
|
|
5
|
+
|
|
6
|
+
p = sub.add_parser("lock", help="lock the door")
|
|
7
|
+
p.add_argument("address")
|
|
8
|
+
p.set_defaults(run=run)
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
async def run(client, creds, args) -> list[str]:
|
|
12
|
+
app_id = bytes.fromhex(creds.app_id)
|
|
13
|
+
await getattr(client, args.command)(
|
|
14
|
+
bytes.fromhex(creds.user_id), app_id, bytes.fromhex(creds.ble_ekey)
|
|
15
|
+
)
|
|
16
|
+
return [f"{args.command} sent"]
|