miramode 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.
- miramode-1.0.0/.gitattributes +2 -0
- miramode-1.0.0/.github/workflows/lint.yml +59 -0
- miramode-1.0.0/.github/workflows/publish.yml +47 -0
- miramode-1.0.0/.gitignore +15 -0
- miramode-1.0.0/LICENSE +21 -0
- miramode-1.0.0/PKG-INFO +231 -0
- miramode-1.0.0/README.md +202 -0
- miramode-1.0.0/miramode/__init__.py +491 -0
- miramode-1.0.0/miramode/cli.py +252 -0
- miramode-1.0.0/miramode/py.typed +0 -0
- miramode-1.0.0/pyproject.toml +71 -0
- miramode-1.0.0/uv.lock +478 -0
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
name: Lint
|
|
2
|
+
|
|
3
|
+
on:
|
|
4
|
+
push:
|
|
5
|
+
branches: [main]
|
|
6
|
+
pull_request:
|
|
7
|
+
workflow_dispatch:
|
|
8
|
+
|
|
9
|
+
permissions:
|
|
10
|
+
contents: read
|
|
11
|
+
|
|
12
|
+
# A new push to the same branch supersedes any run still in progress.
|
|
13
|
+
concurrency:
|
|
14
|
+
group: ${{ github.workflow }}-${{ github.ref }}
|
|
15
|
+
cancel-in-progress: true
|
|
16
|
+
|
|
17
|
+
jobs:
|
|
18
|
+
ruff:
|
|
19
|
+
name: ruff
|
|
20
|
+
runs-on: ubuntu-latest
|
|
21
|
+
steps:
|
|
22
|
+
- uses: actions/checkout@v7.0.1
|
|
23
|
+
|
|
24
|
+
# Pinned to an exact release: setup-uv publishes no moving v9 major
|
|
25
|
+
# tag, so `@v9` does not resolve.
|
|
26
|
+
- name: Install uv
|
|
27
|
+
uses: astral-sh/setup-uv@v9.0.0
|
|
28
|
+
with:
|
|
29
|
+
enable-cache: true
|
|
30
|
+
|
|
31
|
+
# --locked fails if uv.lock has drifted from pyproject.toml, so CI
|
|
32
|
+
# checks the same versions that are pinned for everyone else.
|
|
33
|
+
- name: Install dependencies
|
|
34
|
+
run: uv sync --locked
|
|
35
|
+
|
|
36
|
+
- name: Run ruff
|
|
37
|
+
run: uv run ruff check --output-format=github .
|
|
38
|
+
|
|
39
|
+
- name: Check formatting
|
|
40
|
+
run: uv run ruff format --check .
|
|
41
|
+
|
|
42
|
+
ty:
|
|
43
|
+
name: ty
|
|
44
|
+
runs-on: ubuntu-latest
|
|
45
|
+
steps:
|
|
46
|
+
- uses: actions/checkout@v7.0.1
|
|
47
|
+
|
|
48
|
+
# Pinned to an exact release: setup-uv publishes no moving v9 major
|
|
49
|
+
# tag, so `@v9` does not resolve.
|
|
50
|
+
- name: Install uv
|
|
51
|
+
uses: astral-sh/setup-uv@v9.0.0
|
|
52
|
+
with:
|
|
53
|
+
enable-cache: true
|
|
54
|
+
|
|
55
|
+
- name: Install dependencies
|
|
56
|
+
run: uv sync --locked
|
|
57
|
+
|
|
58
|
+
- name: Run ty
|
|
59
|
+
run: uv run ty check --output-format=github
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
name: Publish
|
|
2
|
+
|
|
3
|
+
on:
|
|
4
|
+
release:
|
|
5
|
+
types: [published]
|
|
6
|
+
|
|
7
|
+
permissions:
|
|
8
|
+
contents: read
|
|
9
|
+
|
|
10
|
+
jobs:
|
|
11
|
+
publish:
|
|
12
|
+
name: Build and publish to PyPI
|
|
13
|
+
runs-on: ubuntu-latest
|
|
14
|
+
permissions:
|
|
15
|
+
# Required for PyPI trusted publishing: the job exchanges a short
|
|
16
|
+
# lived OIDC token for an upload token, so no API token is stored.
|
|
17
|
+
id-token: write
|
|
18
|
+
steps:
|
|
19
|
+
- uses: actions/checkout@v7.0.1
|
|
20
|
+
|
|
21
|
+
- name: Install uv
|
|
22
|
+
uses: astral-sh/setup-uv@v9.0.0
|
|
23
|
+
with:
|
|
24
|
+
enable-cache: true
|
|
25
|
+
|
|
26
|
+
# A release tagged v1.2.3 must contain version 1.2.3, or the upload
|
|
27
|
+
# silently publishes something other than what the tag claims.
|
|
28
|
+
- name: Check tag matches package version
|
|
29
|
+
run: |
|
|
30
|
+
# --color never: uv colourises even when piped, and the escape
|
|
31
|
+
# codes would make this comparison fail for every tag.
|
|
32
|
+
version=$(uv version --short --color never)
|
|
33
|
+
tag="${GITHUB_REF_NAME#v}"
|
|
34
|
+
if [ "$version" != "$tag" ]; then
|
|
35
|
+
echo "::error::Tag $GITHUB_REF_NAME does not match project version $version"
|
|
36
|
+
exit 1
|
|
37
|
+
fi
|
|
38
|
+
echo "Publishing version $version"
|
|
39
|
+
|
|
40
|
+
- name: Build
|
|
41
|
+
run: uv build
|
|
42
|
+
|
|
43
|
+
- name: Check metadata
|
|
44
|
+
run: uvx twine check dist/*
|
|
45
|
+
|
|
46
|
+
- name: Publish
|
|
47
|
+
run: uv publish --trusted-publishing always
|
miramode-1.0.0/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 Ryan Shaw
|
|
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.
|
miramode-1.0.0/PKG-INFO
ADDED
|
@@ -0,0 +1,231 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: miramode
|
|
3
|
+
Version: 1.0.0
|
|
4
|
+
Summary: Control Mira Mode digital showers and bath fillers over Bluetooth LE
|
|
5
|
+
Project-URL: Homepage, https://github.com/ryan-shaw/mira-mode-control
|
|
6
|
+
Project-URL: Repository, https://github.com/ryan-shaw/mira-mode-control
|
|
7
|
+
Project-URL: Issues, https://github.com/ryan-shaw/mira-mode-control/issues
|
|
8
|
+
Author: Ryan Shaw
|
|
9
|
+
License-Expression: MIT
|
|
10
|
+
License-File: LICENSE
|
|
11
|
+
Keywords: ble,bluetooth,home-automation,kohler,mira,mira-mode,shower,smart-home
|
|
12
|
+
Classifier: Development Status :: 4 - Beta
|
|
13
|
+
Classifier: Environment :: Console
|
|
14
|
+
Classifier: Intended Audience :: Developers
|
|
15
|
+
Classifier: Intended Audience :: End Users/Desktop
|
|
16
|
+
Classifier: Operating System :: OS Independent
|
|
17
|
+
Classifier: Programming Language :: Python :: 3
|
|
18
|
+
Classifier: Programming Language :: Python :: 3 :: Only
|
|
19
|
+
Classifier: Programming Language :: Python :: 3.10
|
|
20
|
+
Classifier: Programming Language :: Python :: 3.11
|
|
21
|
+
Classifier: Programming Language :: Python :: 3.12
|
|
22
|
+
Classifier: Programming Language :: Python :: 3.13
|
|
23
|
+
Classifier: Topic :: Home Automation
|
|
24
|
+
Classifier: Topic :: System :: Hardware
|
|
25
|
+
Classifier: Typing :: Typed
|
|
26
|
+
Requires-Python: >=3.10
|
|
27
|
+
Requires-Dist: bleak>=0.22
|
|
28
|
+
Description-Content-Type: text/markdown
|
|
29
|
+
|
|
30
|
+
# mira-mode-control
|
|
31
|
+
|
|
32
|
+
[](https://github.com/ryan-shaw/mira-mode-control/actions/workflows/lint.yml)
|
|
33
|
+
|
|
34
|
+
Control Mira Mode digital showers and bath fillers from Python, over
|
|
35
|
+
Bluetooth Low Energy.
|
|
36
|
+
|
|
37
|
+
Mira Mode valves (now sold under Kohler) ship with no interface other than
|
|
38
|
+
BLE and the vendor's phone app, which means no Home Assistant, no
|
|
39
|
+
automation, and no way to start filling the bath from anywhere but the
|
|
40
|
+
bathroom. This package implements the valve's BLE protocol directly, so
|
|
41
|
+
you can drive it from a Raspberry Pi, a laptop, or anything else with a
|
|
42
|
+
Bluetooth radio.
|
|
43
|
+
|
|
44
|
+
> **Use at your own risk.** This drives real plumbing that produces real
|
|
45
|
+
> hot water. The protocol was reverse engineered, not documented by the
|
|
46
|
+
> vendor. Don't run it unattended until you've watched it behave.
|
|
47
|
+
|
|
48
|
+
## Supported hardware
|
|
49
|
+
|
|
50
|
+
This implements the protocol used by current-generation valves, which
|
|
51
|
+
advertise the GATT service `267f0001-eb15-43f5-94c3-67d2221188f7`. Check
|
|
52
|
+
yours with `miramode info` — it reports whether the valve speaks a
|
|
53
|
+
protocol this package understands.
|
|
54
|
+
|
|
55
|
+
Earlier Mira Mode valves advertise `bccb0001-ca66-11e5-88a4-0002a5d5c51b`
|
|
56
|
+
and use an unrelated, CRC-16 based protocol that this package does not
|
|
57
|
+
support.
|
|
58
|
+
|
|
59
|
+
Developed against a dual outlet shower and bath filler unit. Other valves
|
|
60
|
+
in the range should work, but only the outlets and presets your unit
|
|
61
|
+
actually has will do anything.
|
|
62
|
+
|
|
63
|
+
## Requirements
|
|
64
|
+
|
|
65
|
+
- Python 3.10 or newer
|
|
66
|
+
- A Bluetooth Low Energy adapter
|
|
67
|
+
- On Linux, BlueZ
|
|
68
|
+
- The valve must be paired with the host first, through your operating
|
|
69
|
+
system's Bluetooth settings. The valve refuses to accept commands over
|
|
70
|
+
an unbonded connection.
|
|
71
|
+
|
|
72
|
+
## Installation
|
|
73
|
+
|
|
74
|
+
```console
|
|
75
|
+
pip install miramode
|
|
76
|
+
```
|
|
77
|
+
|
|
78
|
+
Or, with [uv](https://docs.astral.sh/uv/) — `uv tool install miramode`
|
|
79
|
+
to get just the `miramode` command, or `uv add miramode` to use it as a
|
|
80
|
+
library in a project.
|
|
81
|
+
|
|
82
|
+
### From source
|
|
83
|
+
|
|
84
|
+
```console
|
|
85
|
+
git clone git@github.com:ryan-shaw/mira-mode-control.git
|
|
86
|
+
cd mira-mode-control
|
|
87
|
+
uv sync
|
|
88
|
+
```
|
|
89
|
+
|
|
90
|
+
That installs the versions pinned in `uv.lock` into `.venv`. Prefix the
|
|
91
|
+
commands below with `uv run` to use it.
|
|
92
|
+
|
|
93
|
+
### Development
|
|
94
|
+
|
|
95
|
+
The same checks CI runs:
|
|
96
|
+
|
|
97
|
+
```console
|
|
98
|
+
uv run ruff check .
|
|
99
|
+
uv run ruff format --check .
|
|
100
|
+
uv run ty check
|
|
101
|
+
```
|
|
102
|
+
|
|
103
|
+
Use `uv run ruff format .` to apply formatting.
|
|
104
|
+
|
|
105
|
+
## Command line usage
|
|
106
|
+
|
|
107
|
+
Find your valve:
|
|
108
|
+
|
|
109
|
+
```console
|
|
110
|
+
$ miramode scan
|
|
111
|
+
1FE6A4BD-73A5-055E-6463-F4785E21D49D Mira 004C Bathroom
|
|
112
|
+
```
|
|
113
|
+
|
|
114
|
+
Addresses are MAC addresses on Linux and Windows, and opaque UUIDs on
|
|
115
|
+
macOS. Every command below takes one with `-a`.
|
|
116
|
+
|
|
117
|
+
### Presets
|
|
118
|
+
|
|
119
|
+
Presets are the programmes stored in the valve — each carries its own
|
|
120
|
+
target temperature and run time, and they're how the vendor app starts
|
|
121
|
+
the shower or fills the bath. Listing them only reads from the valve and
|
|
122
|
+
runs no water:
|
|
123
|
+
|
|
124
|
+
```console
|
|
125
|
+
$ miramode presets -a <address>
|
|
126
|
+
1 Default
|
|
127
|
+
2 Default Bathfill
|
|
128
|
+
```
|
|
129
|
+
|
|
130
|
+
Factory-fitted presets are numbered from 1; slot 0 is not used. Start one
|
|
131
|
+
by number:
|
|
132
|
+
|
|
133
|
+
```console
|
|
134
|
+
miramode start -a <address> 2
|
|
135
|
+
```
|
|
136
|
+
|
|
137
|
+
### Outlets
|
|
138
|
+
|
|
139
|
+
To run an outlet directly, at a temperature you choose:
|
|
140
|
+
|
|
141
|
+
```console
|
|
142
|
+
miramode outlets -a <address> --first --temperature 39
|
|
143
|
+
```
|
|
144
|
+
|
|
145
|
+
Use `--second` for the second outlet, and both flags together to run both
|
|
146
|
+
at once. A command states the complete desired state, so any outlet you
|
|
147
|
+
don't name is switched off. `--flow` sets the flow rate as a percentage,
|
|
148
|
+
defaulting to 100.
|
|
149
|
+
|
|
150
|
+
Which physical fixture is "first" depends on how your unit is plumbed.
|
|
151
|
+
|
|
152
|
+
To shut everything off, including a running preset:
|
|
153
|
+
|
|
154
|
+
```console
|
|
155
|
+
miramode stop -a <address>
|
|
156
|
+
```
|
|
157
|
+
|
|
158
|
+
### Troubleshooting
|
|
159
|
+
|
|
160
|
+
`-v` logs every frame exchanged with the valve:
|
|
161
|
+
|
|
162
|
+
```console
|
|
163
|
+
$ miramode -v stop -a <address>
|
|
164
|
+
Connected to 1FE6A4BD-73A5-055E-6463-F4785E21D49D
|
|
165
|
+
Sending aa 55 00 ab 04 00 00 00 00 52
|
|
166
|
+
Received channel=1 opcode=0x01 payload=01
|
|
167
|
+
```
|
|
168
|
+
|
|
169
|
+
## Library usage
|
|
170
|
+
|
|
171
|
+
The API is async, built on [bleak](https://github.com/hbldh/bleak):
|
|
172
|
+
|
|
173
|
+
```python
|
|
174
|
+
import asyncio
|
|
175
|
+
from miramode import Outlet, Shower
|
|
176
|
+
|
|
177
|
+
|
|
178
|
+
async def run_a_bath():
|
|
179
|
+
async with Shower("1FE6A4BD-73A5-055E-6463-F4785E21D49D") as shower:
|
|
180
|
+
for preset in await shower.presets():
|
|
181
|
+
print(preset.index, preset.name)
|
|
182
|
+
|
|
183
|
+
await shower.run_preset(2) # start the bath filling
|
|
184
|
+
await asyncio.sleep(300)
|
|
185
|
+
await shower.stop()
|
|
186
|
+
|
|
187
|
+
# or drive an outlet directly
|
|
188
|
+
await shower.set_outlets(Outlet.FIRST, temperature=39.0)
|
|
189
|
+
|
|
190
|
+
|
|
191
|
+
asyncio.run(run_a_bath())
|
|
192
|
+
```
|
|
193
|
+
|
|
194
|
+
Failures raise subclasses of `MiraError`: `NotConnected`,
|
|
195
|
+
`ResponseTimeout`, and `CommandFailed` when the valve rejects a command.
|
|
196
|
+
|
|
197
|
+
## Protocol notes
|
|
198
|
+
|
|
199
|
+
Messages in both directions share one frame layout:
|
|
200
|
+
|
|
201
|
+
```
|
|
202
|
+
aa 55 <channel> <opcode> <length> <payload...> <checksum>
|
|
203
|
+
```
|
|
204
|
+
|
|
205
|
+
The checksum is the two's complement of the sum of the preceding bytes,
|
|
206
|
+
so a whole valid frame sums to zero modulo 256. Commands are written to
|
|
207
|
+
characteristic `267f0002-…` and replies arrive as notifications on
|
|
208
|
+
`267f0003-…`, one reply per command.
|
|
209
|
+
|
|
210
|
+
Three opcodes are implemented:
|
|
211
|
+
|
|
212
|
+
| Opcode | Meaning | Payload |
|
|
213
|
+
| --- | --- | --- |
|
|
214
|
+
| `0xab` | Set outlets | temperature (2 bytes, tenths of a degree, big endian), flow percentage, outlet bitfield |
|
|
215
|
+
| `0xb1` | Run preset | preset slot |
|
|
216
|
+
| `0x5d` | Read preset | preset slot; the reply echoes the slot then a 16 byte NUL-padded name |
|
|
217
|
+
|
|
218
|
+
The outlet field is a bitfield in a single byte — bit 0 is the first
|
|
219
|
+
outlet, bit 1 the second, bit 2 a third — rather than one byte per
|
|
220
|
+
outlet. A temperature of zero means "leave the setting alone", which is
|
|
221
|
+
what `stop` sends.
|
|
222
|
+
|
|
223
|
+
## Acknowledgements
|
|
224
|
+
|
|
225
|
+
Nigel Hannam's [protocol
|
|
226
|
+
documentation](https://github.com/nhannam/shower-controller-documentation)
|
|
227
|
+
was a useful reference for the older generation of these valves.
|
|
228
|
+
|
|
229
|
+
## License
|
|
230
|
+
|
|
231
|
+
MIT — see [LICENSE](LICENSE).
|
miramode-1.0.0/README.md
ADDED
|
@@ -0,0 +1,202 @@
|
|
|
1
|
+
# mira-mode-control
|
|
2
|
+
|
|
3
|
+
[](https://github.com/ryan-shaw/mira-mode-control/actions/workflows/lint.yml)
|
|
4
|
+
|
|
5
|
+
Control Mira Mode digital showers and bath fillers from Python, over
|
|
6
|
+
Bluetooth Low Energy.
|
|
7
|
+
|
|
8
|
+
Mira Mode valves (now sold under Kohler) ship with no interface other than
|
|
9
|
+
BLE and the vendor's phone app, which means no Home Assistant, no
|
|
10
|
+
automation, and no way to start filling the bath from anywhere but the
|
|
11
|
+
bathroom. This package implements the valve's BLE protocol directly, so
|
|
12
|
+
you can drive it from a Raspberry Pi, a laptop, or anything else with a
|
|
13
|
+
Bluetooth radio.
|
|
14
|
+
|
|
15
|
+
> **Use at your own risk.** This drives real plumbing that produces real
|
|
16
|
+
> hot water. The protocol was reverse engineered, not documented by the
|
|
17
|
+
> vendor. Don't run it unattended until you've watched it behave.
|
|
18
|
+
|
|
19
|
+
## Supported hardware
|
|
20
|
+
|
|
21
|
+
This implements the protocol used by current-generation valves, which
|
|
22
|
+
advertise the GATT service `267f0001-eb15-43f5-94c3-67d2221188f7`. Check
|
|
23
|
+
yours with `miramode info` — it reports whether the valve speaks a
|
|
24
|
+
protocol this package understands.
|
|
25
|
+
|
|
26
|
+
Earlier Mira Mode valves advertise `bccb0001-ca66-11e5-88a4-0002a5d5c51b`
|
|
27
|
+
and use an unrelated, CRC-16 based protocol that this package does not
|
|
28
|
+
support.
|
|
29
|
+
|
|
30
|
+
Developed against a dual outlet shower and bath filler unit. Other valves
|
|
31
|
+
in the range should work, but only the outlets and presets your unit
|
|
32
|
+
actually has will do anything.
|
|
33
|
+
|
|
34
|
+
## Requirements
|
|
35
|
+
|
|
36
|
+
- Python 3.10 or newer
|
|
37
|
+
- A Bluetooth Low Energy adapter
|
|
38
|
+
- On Linux, BlueZ
|
|
39
|
+
- The valve must be paired with the host first, through your operating
|
|
40
|
+
system's Bluetooth settings. The valve refuses to accept commands over
|
|
41
|
+
an unbonded connection.
|
|
42
|
+
|
|
43
|
+
## Installation
|
|
44
|
+
|
|
45
|
+
```console
|
|
46
|
+
pip install miramode
|
|
47
|
+
```
|
|
48
|
+
|
|
49
|
+
Or, with [uv](https://docs.astral.sh/uv/) — `uv tool install miramode`
|
|
50
|
+
to get just the `miramode` command, or `uv add miramode` to use it as a
|
|
51
|
+
library in a project.
|
|
52
|
+
|
|
53
|
+
### From source
|
|
54
|
+
|
|
55
|
+
```console
|
|
56
|
+
git clone git@github.com:ryan-shaw/mira-mode-control.git
|
|
57
|
+
cd mira-mode-control
|
|
58
|
+
uv sync
|
|
59
|
+
```
|
|
60
|
+
|
|
61
|
+
That installs the versions pinned in `uv.lock` into `.venv`. Prefix the
|
|
62
|
+
commands below with `uv run` to use it.
|
|
63
|
+
|
|
64
|
+
### Development
|
|
65
|
+
|
|
66
|
+
The same checks CI runs:
|
|
67
|
+
|
|
68
|
+
```console
|
|
69
|
+
uv run ruff check .
|
|
70
|
+
uv run ruff format --check .
|
|
71
|
+
uv run ty check
|
|
72
|
+
```
|
|
73
|
+
|
|
74
|
+
Use `uv run ruff format .` to apply formatting.
|
|
75
|
+
|
|
76
|
+
## Command line usage
|
|
77
|
+
|
|
78
|
+
Find your valve:
|
|
79
|
+
|
|
80
|
+
```console
|
|
81
|
+
$ miramode scan
|
|
82
|
+
1FE6A4BD-73A5-055E-6463-F4785E21D49D Mira 004C Bathroom
|
|
83
|
+
```
|
|
84
|
+
|
|
85
|
+
Addresses are MAC addresses on Linux and Windows, and opaque UUIDs on
|
|
86
|
+
macOS. Every command below takes one with `-a`.
|
|
87
|
+
|
|
88
|
+
### Presets
|
|
89
|
+
|
|
90
|
+
Presets are the programmes stored in the valve — each carries its own
|
|
91
|
+
target temperature and run time, and they're how the vendor app starts
|
|
92
|
+
the shower or fills the bath. Listing them only reads from the valve and
|
|
93
|
+
runs no water:
|
|
94
|
+
|
|
95
|
+
```console
|
|
96
|
+
$ miramode presets -a <address>
|
|
97
|
+
1 Default
|
|
98
|
+
2 Default Bathfill
|
|
99
|
+
```
|
|
100
|
+
|
|
101
|
+
Factory-fitted presets are numbered from 1; slot 0 is not used. Start one
|
|
102
|
+
by number:
|
|
103
|
+
|
|
104
|
+
```console
|
|
105
|
+
miramode start -a <address> 2
|
|
106
|
+
```
|
|
107
|
+
|
|
108
|
+
### Outlets
|
|
109
|
+
|
|
110
|
+
To run an outlet directly, at a temperature you choose:
|
|
111
|
+
|
|
112
|
+
```console
|
|
113
|
+
miramode outlets -a <address> --first --temperature 39
|
|
114
|
+
```
|
|
115
|
+
|
|
116
|
+
Use `--second` for the second outlet, and both flags together to run both
|
|
117
|
+
at once. A command states the complete desired state, so any outlet you
|
|
118
|
+
don't name is switched off. `--flow` sets the flow rate as a percentage,
|
|
119
|
+
defaulting to 100.
|
|
120
|
+
|
|
121
|
+
Which physical fixture is "first" depends on how your unit is plumbed.
|
|
122
|
+
|
|
123
|
+
To shut everything off, including a running preset:
|
|
124
|
+
|
|
125
|
+
```console
|
|
126
|
+
miramode stop -a <address>
|
|
127
|
+
```
|
|
128
|
+
|
|
129
|
+
### Troubleshooting
|
|
130
|
+
|
|
131
|
+
`-v` logs every frame exchanged with the valve:
|
|
132
|
+
|
|
133
|
+
```console
|
|
134
|
+
$ miramode -v stop -a <address>
|
|
135
|
+
Connected to 1FE6A4BD-73A5-055E-6463-F4785E21D49D
|
|
136
|
+
Sending aa 55 00 ab 04 00 00 00 00 52
|
|
137
|
+
Received channel=1 opcode=0x01 payload=01
|
|
138
|
+
```
|
|
139
|
+
|
|
140
|
+
## Library usage
|
|
141
|
+
|
|
142
|
+
The API is async, built on [bleak](https://github.com/hbldh/bleak):
|
|
143
|
+
|
|
144
|
+
```python
|
|
145
|
+
import asyncio
|
|
146
|
+
from miramode import Outlet, Shower
|
|
147
|
+
|
|
148
|
+
|
|
149
|
+
async def run_a_bath():
|
|
150
|
+
async with Shower("1FE6A4BD-73A5-055E-6463-F4785E21D49D") as shower:
|
|
151
|
+
for preset in await shower.presets():
|
|
152
|
+
print(preset.index, preset.name)
|
|
153
|
+
|
|
154
|
+
await shower.run_preset(2) # start the bath filling
|
|
155
|
+
await asyncio.sleep(300)
|
|
156
|
+
await shower.stop()
|
|
157
|
+
|
|
158
|
+
# or drive an outlet directly
|
|
159
|
+
await shower.set_outlets(Outlet.FIRST, temperature=39.0)
|
|
160
|
+
|
|
161
|
+
|
|
162
|
+
asyncio.run(run_a_bath())
|
|
163
|
+
```
|
|
164
|
+
|
|
165
|
+
Failures raise subclasses of `MiraError`: `NotConnected`,
|
|
166
|
+
`ResponseTimeout`, and `CommandFailed` when the valve rejects a command.
|
|
167
|
+
|
|
168
|
+
## Protocol notes
|
|
169
|
+
|
|
170
|
+
Messages in both directions share one frame layout:
|
|
171
|
+
|
|
172
|
+
```
|
|
173
|
+
aa 55 <channel> <opcode> <length> <payload...> <checksum>
|
|
174
|
+
```
|
|
175
|
+
|
|
176
|
+
The checksum is the two's complement of the sum of the preceding bytes,
|
|
177
|
+
so a whole valid frame sums to zero modulo 256. Commands are written to
|
|
178
|
+
characteristic `267f0002-…` and replies arrive as notifications on
|
|
179
|
+
`267f0003-…`, one reply per command.
|
|
180
|
+
|
|
181
|
+
Three opcodes are implemented:
|
|
182
|
+
|
|
183
|
+
| Opcode | Meaning | Payload |
|
|
184
|
+
| --- | --- | --- |
|
|
185
|
+
| `0xab` | Set outlets | temperature (2 bytes, tenths of a degree, big endian), flow percentage, outlet bitfield |
|
|
186
|
+
| `0xb1` | Run preset | preset slot |
|
|
187
|
+
| `0x5d` | Read preset | preset slot; the reply echoes the slot then a 16 byte NUL-padded name |
|
|
188
|
+
|
|
189
|
+
The outlet field is a bitfield in a single byte — bit 0 is the first
|
|
190
|
+
outlet, bit 1 the second, bit 2 a third — rather than one byte per
|
|
191
|
+
outlet. A temperature of zero means "leave the setting alone", which is
|
|
192
|
+
what `stop` sends.
|
|
193
|
+
|
|
194
|
+
## Acknowledgements
|
|
195
|
+
|
|
196
|
+
Nigel Hannam's [protocol
|
|
197
|
+
documentation](https://github.com/nhannam/shower-controller-documentation)
|
|
198
|
+
was a useful reference for the older generation of these valves.
|
|
199
|
+
|
|
200
|
+
## License
|
|
201
|
+
|
|
202
|
+
MIT — see [LICENSE](LICENSE).
|