kettle-x2p-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.
@@ -0,0 +1,27 @@
1
+ # Changelog
2
+
3
+ All notable changes to this project will be documented in this file.
4
+
5
+ The format is based on [Keep a Changelog](https://keepachangelog.com/),
6
+ and this project adheres to [Semantic Versioning](https://semver.org/).
7
+
8
+ ## [Unreleased]
9
+
10
+ ## [1.0.0] - 2026-10-16
11
+ ### Added
12
+ - Initial release, available from [PyPI](https://pypi.org/project/kettle-x2p-cli/) (`pip install kettle-x2p-cli`),
13
+ the [mane tap](https://github.com/ManeFunction/homebrew-tap) (`brew install kettle`) and GitHub releases.
14
+ Local control of the Xiaomi Smart Kettle 2 Pro (EU, `xiaomi.kettle.v20`) over the LAN:
15
+ `boil`, `warm-to <temp>`, `warm-keep <temp> <minutes>`, `cancel` and `status`, plus an interactive `shell`.
16
+ - `kettle setup`: guided first-time setup with on-screen instructions. It explains how to pair the kettle in the
17
+ Xiaomi Home app, where to get the token extractor, asks for the token (hidden input), finds the kettle on the
18
+ network by trying the token on each Xiaomi device, then verifies and saves it.
19
+ - `info`, `find` (re-locates the kettle after an IP change) and `hello` (raw handshake, for diagnostics).
20
+ - `provision` for an advanced, experimental Wi-Fi setup of a kettle in setup mode.
21
+ - Safety checks before heating: the kettle must be `xiaomi.kettle.v20`, idle, without a fault and on its base;
22
+ the exact writes are printed and confirmed (`--yes` is required without a terminal). `--dry-run` prints the
23
+ requests without sending anything.
24
+ - Config in `~/.config/kettle/config.json` (mode 600). The client re-handshakes and re-syncs the device
25
+ timestamp on its own.
26
+ - No dependencies: AES-128-CBC is implemented in pure Python (checked against the FIPS-197 and NIST SP 800-38A
27
+ test vectors).
@@ -0,0 +1,22 @@
1
+
2
+ The MIT License (MIT)
3
+
4
+ Copyright (c) 2026 Ilia Petrov-Komotskii (Mane Function)
5
+
6
+ Permission is hereby granted, free of charge, to any person obtaining a copy
7
+ of this software and associated documentation files (the "Software"), to deal
8
+ in the Software without restriction, including without limitation the rights
9
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
10
+ copies of the Software, and to permit persons to whom the Software is
11
+ furnished to do so, subject to the following conditions:
12
+
13
+ The above copyright notice and this permission notice shall be included in all
14
+ copies or substantial portions of the Software.
15
+
16
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
17
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
18
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
19
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
20
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
21
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
22
+ SOFTWARE.
@@ -0,0 +1,3 @@
1
+ include CHANGELOG.md SPEC.md LICENSE
2
+ graft tests
3
+ global-exclude __pycache__ *.py[cod]
@@ -0,0 +1,198 @@
1
+ Metadata-Version: 2.4
2
+ Name: kettle-x2p-cli
3
+ Version: 1.0.0
4
+ Summary: Local control of the Xiaomi Smart Kettle 2 Pro over your Wi-Fi, no cloud at runtime.
5
+ Author-email: Ilia Petrov-Komotskii <ilia@inkedkettle.art>
6
+ License-Expression: MIT
7
+ Project-URL: Homepage, https://github.com/ManeFunction/kettle-x2p-cli
8
+ Project-URL: Repository, https://github.com/ManeFunction/kettle-x2p-cli
9
+ Project-URL: Issues, https://github.com/ManeFunction/kettle-x2p-cli/issues
10
+ Project-URL: Changelog, https://github.com/ManeFunction/kettle-x2p-cli/blob/main/CHANGELOG.md
11
+ Keywords: xiaomi,mijia,miot,miio,kettle,smart-home,home-assistant,cli
12
+ Classifier: Programming Language :: Python :: 3
13
+ Classifier: Operating System :: OS Independent
14
+ Classifier: Environment :: Console
15
+ Classifier: Topic :: Home Automation
16
+ Requires-Python: >=3.9
17
+ Description-Content-Type: text/markdown
18
+ License-File: LICENSE
19
+ Dynamic: license-file
20
+
21
+ # kettle
22
+
23
+ Control your [Xiaomi Smart Kettle 2 Pro](https://www.mi.com/global/product/xiaomi-smart-kettle-2-pro/) from the
24
+ Terminal: boil, heat to a temperature, keep warm for up to 24 hours, check the state or stop it. Everything runs over
25
+ your own Wi-Fi, directly to the kettle - no Xiaomi Home app and no cloud connection when you use it.
26
+
27
+ ```
28
+ $ kettle warm-keep 80 120
29
+ warm to 80 C and keep warm 120 min: water at 41 C, idle, no fault.
30
+ About to send set_properties:
31
+ (2,4) = 80
32
+ ...
33
+ Start heating? [y/N] y
34
+ status: heating, 41 C, fault 0
35
+ ```
36
+
37
+ Written for the EU model (BHR9107EU, `xiaomi.kettle.v20`, 1.7 L, 1800 W), tested on firmware `2.2.7_0016`.
38
+ It has no dependencies except Python itself. This is the standalone tool and a documented protocol reference ([SPEC.md](https://github.com/ManeFunction/kettle-x2p-cli/blob/main/SPEC.md))
39
+ made just for fun... or not. Setting a kettle right from the Terminal while working is pretty neat, actually :)
40
+
41
+ ---
42
+
43
+ ## Features
44
+
45
+ | Command | What it does |
46
+ |---|---|
47
+ | `kettle setup` | Guided first-time setup: asks for the token, finds the kettle, checks and saves it. |
48
+ | `kettle boil` | Heats to boiling (99 °C, the kettle's boil mode). |
49
+ | `kettle warm-to <40-99>` | Heats to that temperature in °C, then stops. |
50
+ | `kettle warm-keep <40-99> <60-1440>` | Heats to that temperature and keeps it warm for that many minutes (1-24 h). |
51
+ | `kettle cancel` | Stops heating / keep-warm. |
52
+ | `kettle status` | State, current temperature, target, fault, keep-warm settings. |
53
+ | `kettle shell` | Interactive session with the same commands. |
54
+ | `kettle info` | Model, firmware and MAC from the kettle. |
55
+ | `kettle find` | Finds the kettle on the network again if its IP changed. |
56
+
57
+ Useful flags go before the command: `--dry-run` (print the requests, send nothing), `--yes` (skip the heating
58
+ confirmation), `--debug` (raw packets), `--ip`, `--token`.
59
+
60
+ **Safety.** Heating commands refuse to start unless the kettle is a `xiaomi.kettle.v20`, idle, on its base and
61
+ reporting no fault, and a status read that fails or is unclear never starts anything. They print exactly what they
62
+ will send and ask `y/N` (without a terminal, `--yes` is required). This model has no water-level reading, so a
63
+ low-water condition only shows up as a fault code. Heat water remotely only when you know the kettle has water in it.
64
+
65
+ ---
66
+
67
+ ## Installation
68
+
69
+ **kettle** is available from a variety of sources.
70
+ `pip` or `brew` is recommended, because they have a convenient way to manage updates automatically.
71
+
72
+ 1) **pip (Recommended for everyone with a Python environment)**
73
+ - You can check if you have Python installed by running `python --version` in the Terminal or cmd.
74
+ - For Mac and Linux users, there is a high chance that you already have Python installed on your system.
75
+ - For Windows users, you can download Python from the [official website](https://www.python.org/downloads/).
76
+ The tool is built and tested for macOS and Linux; it should work on Windows too, but that is untested.
77
+ - After confirmation, install **kettle** through the [PyPI](https://pypi.org/project/kettle-x2p-cli) package
78
+ manager, typing `pip install kettle-x2p-cli` in the Terminal. For Mac users, you may need to use `pip3`
79
+ instead of `pip`. The package is called `kettle-x2p-cli`; the command it installs is `kettle`.
80
+ - Verify the installation with `kettle --version`.
81
+ - You are perfect, you can use the app with `kettle` from any folder in your system.
82
+ 2) **brew (Recommended for Mac and Linux users)**
83
+ - Type `brew tap manefunction/tap` in your Terminal to add my custom tap (app source) to your brew sources,
84
+ if you haven't already.
85
+ - Type `brew install kettle` to install the application itself.
86
+ - Verify the installation with `kettle --version`.
87
+ - You are perfect, you can use the app with `kettle` from any folder in your system.
88
+ 3) **Python package (manual installation, for advanced users)**
89
+ - Clone the repository or download the source code from GitHub.
90
+ - Go to the folder with the code in your Terminal.
91
+ - Run `pip install .` to install `kettle` to your system.
92
+ - Run `kettle --version` to verify the install.
93
+
94
+ Updates arrive with `pip install --upgrade kettle-x2p-cli` or `brew upgrade`. Your saved kettle settings live outside the install
95
+ (`~/.config/kettle/`), so upgrading or reinstalling never loses them. (`pip` may ask you to use a virtual environment or `pipx` on newer
96
+ systems; `pipx install kettle-x2p-cli` is a good fit for a command line tool.)
97
+
98
+ ---
99
+
100
+ ## First-time setup
101
+
102
+ Run `kettle setup` and follow the instructions on the screen. It takes about five minutes the first time and is
103
+ never needed again unless you re-pair the kettle.
104
+
105
+ 1. **Add the kettle to the Xiaomi Home app** (iOS / Android) so it joins your 2.4 GHz Wi-Fi.
106
+ 2. **Get the kettle's token** with the free, open-source
107
+ [Xiaomi Cloud Tokens Extractor](https://github.com/PiotrMachowski/Xiaomi-Cloud-Tokens-Extractor). The setup
108
+ screen shows the exact commands for Windows, macOS and Linux. It asks for your Xiaomi login and lists your devices
109
+ with their tokens; copy the 32-character token of *Xiaomi Smart Kettle 2 Pro* (`xiaomi.kettle.v20`).
110
+ 3. **Paste the token** into `kettle setup` (the input is hidden). It scans your network, tries the token on every
111
+ Xiaomi device it finds, and the one that answers is your kettle.
112
+ 4. It checks the model and saves everything to `~/.config/kettle/config.json` (readable by you only).
113
+
114
+ Then try `kettle status`. Reserve the kettle's address in your router (DHCP reservation) so it never moves; if it
115
+ does, `kettle find` locates it again.
116
+
117
+ For scripts, skip the prompts: `kettle --token <token> --ip <ip> setup`. Note that a token typed on the command
118
+ line is visible in your shell history, so the interactive prompt is the safer way.
119
+
120
+ ### Why the Xiaomi app and cloud are needed once
121
+
122
+ The kettle talks to the local network with a secret 16-byte token, and only Xiaomi's cloud keeps it for you.
123
+ On firmware `2.2.7` the kettle changes that token when it joins your Wi-Fi, so the token it shows while in
124
+ setup mode stops working afterwards. I tried the alternatives (including the app's own extra provisioning parameters,
125
+ `kettle provision --app-params`); none gave a usable token without the cloud. After the one-time extraction,
126
+ all control is local. If you re-pair or reset the kettle, the token changes and you run `kettle setup` again.
127
+
128
+ ---
129
+
130
+ ## Usage
131
+
132
+ ```bash
133
+ kettle status # what is it doing?
134
+ kettle boil # asks for confirmation, then heats to boiling
135
+ kettle warm-to 70 # 70 °C, then stop (green tea)
136
+ kettle warm-keep 85 90 # 85 °C and keep it there for 90 minutes
137
+ kettle cancel # stop everything
138
+ kettle --dry-run warm-to 70 # show what would be sent
139
+ kettle shell # keep a session open
140
+ ```
141
+
142
+ In `kettle shell` you type the same commands without the `kettle` prefix (`boil`, `warm-to 70`, `status`, `quit`).
143
+ Keep-warm time is limited by the kettle to 60-1440 minutes, and the target temperature to 40-99 °C; anything
144
+ else is rejected before it is sent.
145
+
146
+ ### Troubleshooting
147
+
148
+ - **"no reply from ..."**: the kettle is offline, the IP changed (`kettle find`), or the token is out of date
149
+ (the kettle silently ignores requests it cannot decrypt). Run `kettle setup` again if you re-paired it.
150
+ - **Setup does not find the kettle**: your computer and the kettle must be on the same network (no guest network
151
+ or VLAN between them). You can enter the IP by hand; it is in your router's client list.
152
+ - **"kettle reports fault ..."**: check the water level and that the kettle sits on its base. Fault codes are not
153
+ documented by Xiaomi; `0` means none.
154
+
155
+ ---
156
+
157
+ ## How it works
158
+
159
+ The kettle speaks Xiaomi's *miio* protocol over UDP port 54321: a hello handshake that returns the device id and
160
+ a timestamp, then JSON-RPC calls encrypted with AES-128-CBC keyed from the token. Its functions are described by
161
+ a MIoT spec (service/property numbers); the ones used here are listed in [SPEC.md](https://github.com/ManeFunction/kettle-x2p-cli/blob/main/SPEC.md). The client is a few hundred
162
+ lines of standard-library Python, including its own AES, and handles re-handshakes and timestamp drift by itself.
163
+
164
+ ---
165
+
166
+ ## Development
167
+
168
+ ```bash
169
+ python3 -m venv .venv && .venv/bin/pip install -e .
170
+ .venv/bin/python -m unittest discover -s tests -v
171
+ ```
172
+
173
+ The tests run against a fake kettle on the loopback interface that speaks the real wire format; nothing touches a
174
+ real device. The release workflow checks that the tag matches `src/kettle/__init__.py`, builds the package and
175
+ publishes it to PyPI and creates the GitHub release; the Homebrew formula is bumped automatically.
176
+
177
+ ---
178
+
179
+ ## Credits
180
+
181
+ - [python-miio](https://github.com/rytilahti/python-miio) for documenting the miio protocol.
182
+ - [Xiaomi Cloud Tokens Extractor](https://github.com/PiotrMachowski/Xiaomi-Cloud-Tokens-Extractor) by Piotr Machowski.
183
+ - [miot-spec.org](https://home.miot-spec.com/) for the device specs, and username-ardrey for
184
+ [Xiaomi Smart Kettle integration for Home Assistant](https://github.com/username-andrey/xiaomi-smart-kettle-ha),
185
+ which shows how this model is started.
186
+
187
+ This is an unofficial project and is not affiliated with Xiaomi.
188
+
189
+
190
+ ## Repository info
191
+
192
+ This repo follows the [Conventional Commits](https://www.conventionalcommits.org/) specification.
193
+
194
+ [![GitHub release (latest by date)](https://img.shields.io/github/v/release/ManeFunction/kettle-x2p-cli)](https://github.com/ManeFunction/kettle-x2p-cli/releases/latest)
195
+ [![GitHub All Releases](https://img.shields.io/github/downloads/ManeFunction/kettle-x2p-cli/total)](https://github.com/ManeFunction/kettle-x2p-cli/releases)
196
+ [![PyPI version](https://img.shields.io/pypi/v/kettle-x2p-cli)](https://pypi.org/project/kettle-x2p-cli/)
197
+ [![PyPI downloads](https://img.shields.io/pypi/dm/kettle-x2p-cli)](https://pypi.org/project/kettle-x2p-cli/)
198
+ [![GitHub Sponsors](https://img.shields.io/github/sponsors/ManeFunction?label=Sponsor&logo=GitHubSponsors&style=flat)](https://github.com/sponsors/ManeFunction)
@@ -0,0 +1,178 @@
1
+ # kettle
2
+
3
+ Control your [Xiaomi Smart Kettle 2 Pro](https://www.mi.com/global/product/xiaomi-smart-kettle-2-pro/) from the
4
+ Terminal: boil, heat to a temperature, keep warm for up to 24 hours, check the state or stop it. Everything runs over
5
+ your own Wi-Fi, directly to the kettle - no Xiaomi Home app and no cloud connection when you use it.
6
+
7
+ ```
8
+ $ kettle warm-keep 80 120
9
+ warm to 80 C and keep warm 120 min: water at 41 C, idle, no fault.
10
+ About to send set_properties:
11
+ (2,4) = 80
12
+ ...
13
+ Start heating? [y/N] y
14
+ status: heating, 41 C, fault 0
15
+ ```
16
+
17
+ Written for the EU model (BHR9107EU, `xiaomi.kettle.v20`, 1.7 L, 1800 W), tested on firmware `2.2.7_0016`.
18
+ It has no dependencies except Python itself. This is the standalone tool and a documented protocol reference ([SPEC.md](https://github.com/ManeFunction/kettle-x2p-cli/blob/main/SPEC.md))
19
+ made just for fun... or not. Setting a kettle right from the Terminal while working is pretty neat, actually :)
20
+
21
+ ---
22
+
23
+ ## Features
24
+
25
+ | Command | What it does |
26
+ |---|---|
27
+ | `kettle setup` | Guided first-time setup: asks for the token, finds the kettle, checks and saves it. |
28
+ | `kettle boil` | Heats to boiling (99 °C, the kettle's boil mode). |
29
+ | `kettle warm-to <40-99>` | Heats to that temperature in °C, then stops. |
30
+ | `kettle warm-keep <40-99> <60-1440>` | Heats to that temperature and keeps it warm for that many minutes (1-24 h). |
31
+ | `kettle cancel` | Stops heating / keep-warm. |
32
+ | `kettle status` | State, current temperature, target, fault, keep-warm settings. |
33
+ | `kettle shell` | Interactive session with the same commands. |
34
+ | `kettle info` | Model, firmware and MAC from the kettle. |
35
+ | `kettle find` | Finds the kettle on the network again if its IP changed. |
36
+
37
+ Useful flags go before the command: `--dry-run` (print the requests, send nothing), `--yes` (skip the heating
38
+ confirmation), `--debug` (raw packets), `--ip`, `--token`.
39
+
40
+ **Safety.** Heating commands refuse to start unless the kettle is a `xiaomi.kettle.v20`, idle, on its base and
41
+ reporting no fault, and a status read that fails or is unclear never starts anything. They print exactly what they
42
+ will send and ask `y/N` (without a terminal, `--yes` is required). This model has no water-level reading, so a
43
+ low-water condition only shows up as a fault code. Heat water remotely only when you know the kettle has water in it.
44
+
45
+ ---
46
+
47
+ ## Installation
48
+
49
+ **kettle** is available from a variety of sources.
50
+ `pip` or `brew` is recommended, because they have a convenient way to manage updates automatically.
51
+
52
+ 1) **pip (Recommended for everyone with a Python environment)**
53
+ - You can check if you have Python installed by running `python --version` in the Terminal or cmd.
54
+ - For Mac and Linux users, there is a high chance that you already have Python installed on your system.
55
+ - For Windows users, you can download Python from the [official website](https://www.python.org/downloads/).
56
+ The tool is built and tested for macOS and Linux; it should work on Windows too, but that is untested.
57
+ - After confirmation, install **kettle** through the [PyPI](https://pypi.org/project/kettle-x2p-cli) package
58
+ manager, typing `pip install kettle-x2p-cli` in the Terminal. For Mac users, you may need to use `pip3`
59
+ instead of `pip`. The package is called `kettle-x2p-cli`; the command it installs is `kettle`.
60
+ - Verify the installation with `kettle --version`.
61
+ - You are perfect, you can use the app with `kettle` from any folder in your system.
62
+ 2) **brew (Recommended for Mac and Linux users)**
63
+ - Type `brew tap manefunction/tap` in your Terminal to add my custom tap (app source) to your brew sources,
64
+ if you haven't already.
65
+ - Type `brew install kettle` to install the application itself.
66
+ - Verify the installation with `kettle --version`.
67
+ - You are perfect, you can use the app with `kettle` from any folder in your system.
68
+ 3) **Python package (manual installation, for advanced users)**
69
+ - Clone the repository or download the source code from GitHub.
70
+ - Go to the folder with the code in your Terminal.
71
+ - Run `pip install .` to install `kettle` to your system.
72
+ - Run `kettle --version` to verify the install.
73
+
74
+ Updates arrive with `pip install --upgrade kettle-x2p-cli` or `brew upgrade`. Your saved kettle settings live outside the install
75
+ (`~/.config/kettle/`), so upgrading or reinstalling never loses them. (`pip` may ask you to use a virtual environment or `pipx` on newer
76
+ systems; `pipx install kettle-x2p-cli` is a good fit for a command line tool.)
77
+
78
+ ---
79
+
80
+ ## First-time setup
81
+
82
+ Run `kettle setup` and follow the instructions on the screen. It takes about five minutes the first time and is
83
+ never needed again unless you re-pair the kettle.
84
+
85
+ 1. **Add the kettle to the Xiaomi Home app** (iOS / Android) so it joins your 2.4 GHz Wi-Fi.
86
+ 2. **Get the kettle's token** with the free, open-source
87
+ [Xiaomi Cloud Tokens Extractor](https://github.com/PiotrMachowski/Xiaomi-Cloud-Tokens-Extractor). The setup
88
+ screen shows the exact commands for Windows, macOS and Linux. It asks for your Xiaomi login and lists your devices
89
+ with their tokens; copy the 32-character token of *Xiaomi Smart Kettle 2 Pro* (`xiaomi.kettle.v20`).
90
+ 3. **Paste the token** into `kettle setup` (the input is hidden). It scans your network, tries the token on every
91
+ Xiaomi device it finds, and the one that answers is your kettle.
92
+ 4. It checks the model and saves everything to `~/.config/kettle/config.json` (readable by you only).
93
+
94
+ Then try `kettle status`. Reserve the kettle's address in your router (DHCP reservation) so it never moves; if it
95
+ does, `kettle find` locates it again.
96
+
97
+ For scripts, skip the prompts: `kettle --token <token> --ip <ip> setup`. Note that a token typed on the command
98
+ line is visible in your shell history, so the interactive prompt is the safer way.
99
+
100
+ ### Why the Xiaomi app and cloud are needed once
101
+
102
+ The kettle talks to the local network with a secret 16-byte token, and only Xiaomi's cloud keeps it for you.
103
+ On firmware `2.2.7` the kettle changes that token when it joins your Wi-Fi, so the token it shows while in
104
+ setup mode stops working afterwards. I tried the alternatives (including the app's own extra provisioning parameters,
105
+ `kettle provision --app-params`); none gave a usable token without the cloud. After the one-time extraction,
106
+ all control is local. If you re-pair or reset the kettle, the token changes and you run `kettle setup` again.
107
+
108
+ ---
109
+
110
+ ## Usage
111
+
112
+ ```bash
113
+ kettle status # what is it doing?
114
+ kettle boil # asks for confirmation, then heats to boiling
115
+ kettle warm-to 70 # 70 °C, then stop (green tea)
116
+ kettle warm-keep 85 90 # 85 °C and keep it there for 90 minutes
117
+ kettle cancel # stop everything
118
+ kettle --dry-run warm-to 70 # show what would be sent
119
+ kettle shell # keep a session open
120
+ ```
121
+
122
+ In `kettle shell` you type the same commands without the `kettle` prefix (`boil`, `warm-to 70`, `status`, `quit`).
123
+ Keep-warm time is limited by the kettle to 60-1440 minutes, and the target temperature to 40-99 °C; anything
124
+ else is rejected before it is sent.
125
+
126
+ ### Troubleshooting
127
+
128
+ - **"no reply from ..."**: the kettle is offline, the IP changed (`kettle find`), or the token is out of date
129
+ (the kettle silently ignores requests it cannot decrypt). Run `kettle setup` again if you re-paired it.
130
+ - **Setup does not find the kettle**: your computer and the kettle must be on the same network (no guest network
131
+ or VLAN between them). You can enter the IP by hand; it is in your router's client list.
132
+ - **"kettle reports fault ..."**: check the water level and that the kettle sits on its base. Fault codes are not
133
+ documented by Xiaomi; `0` means none.
134
+
135
+ ---
136
+
137
+ ## How it works
138
+
139
+ The kettle speaks Xiaomi's *miio* protocol over UDP port 54321: a hello handshake that returns the device id and
140
+ a timestamp, then JSON-RPC calls encrypted with AES-128-CBC keyed from the token. Its functions are described by
141
+ a MIoT spec (service/property numbers); the ones used here are listed in [SPEC.md](https://github.com/ManeFunction/kettle-x2p-cli/blob/main/SPEC.md). The client is a few hundred
142
+ lines of standard-library Python, including its own AES, and handles re-handshakes and timestamp drift by itself.
143
+
144
+ ---
145
+
146
+ ## Development
147
+
148
+ ```bash
149
+ python3 -m venv .venv && .venv/bin/pip install -e .
150
+ .venv/bin/python -m unittest discover -s tests -v
151
+ ```
152
+
153
+ The tests run against a fake kettle on the loopback interface that speaks the real wire format; nothing touches a
154
+ real device. The release workflow checks that the tag matches `src/kettle/__init__.py`, builds the package and
155
+ publishes it to PyPI and creates the GitHub release; the Homebrew formula is bumped automatically.
156
+
157
+ ---
158
+
159
+ ## Credits
160
+
161
+ - [python-miio](https://github.com/rytilahti/python-miio) for documenting the miio protocol.
162
+ - [Xiaomi Cloud Tokens Extractor](https://github.com/PiotrMachowski/Xiaomi-Cloud-Tokens-Extractor) by Piotr Machowski.
163
+ - [miot-spec.org](https://home.miot-spec.com/) for the device specs, and username-ardrey for
164
+ [Xiaomi Smart Kettle integration for Home Assistant](https://github.com/username-andrey/xiaomi-smart-kettle-ha),
165
+ which shows how this model is started.
166
+
167
+ This is an unofficial project and is not affiliated with Xiaomi.
168
+
169
+
170
+ ## Repository info
171
+
172
+ This repo follows the [Conventional Commits](https://www.conventionalcommits.org/) specification.
173
+
174
+ [![GitHub release (latest by date)](https://img.shields.io/github/v/release/ManeFunction/kettle-x2p-cli)](https://github.com/ManeFunction/kettle-x2p-cli/releases/latest)
175
+ [![GitHub All Releases](https://img.shields.io/github/downloads/ManeFunction/kettle-x2p-cli/total)](https://github.com/ManeFunction/kettle-x2p-cli/releases)
176
+ [![PyPI version](https://img.shields.io/pypi/v/kettle-x2p-cli)](https://pypi.org/project/kettle-x2p-cli/)
177
+ [![PyPI downloads](https://img.shields.io/pypi/dm/kettle-x2p-cli)](https://pypi.org/project/kettle-x2p-cli/)
178
+ [![GitHub Sponsors](https://img.shields.io/github/sponsors/ManeFunction?label=Sponsor&logo=GitHubSponsors&style=flat)](https://github.com/sponsors/ManeFunction)
@@ -0,0 +1,68 @@
1
+ # MIoT spec: Xiaomi Smart Kettle 2 Pro (EU, BHR9107EU)
2
+
3
+ **Model string: `xiaomi.kettle.v20`** ("Xiaomi Smart Kettle 2 Pro (欧规版)").
4
+ URN: `urn:miot-spec-v2:device:kettle:0000A009:xiaomi-v20:1`
5
+
6
+ > Status: **model confirmed on the device** (`miIO.info` returned `model: xiaomi.kettle.v20`,
7
+ > firmware `2.2.7_0016`, hw `esp32`). `boil`, `warm-to`, `warm-keep`, `cancel` and `status` are all verified on the device.
8
+
9
+ Sources (two agree on all numbers): home.miot-spec.com/spec/xiaomi.kettle.v20 and
10
+ miot-spec.org/miot-spec-v2/instance?type=<URN above>.
11
+
12
+ ## siid 2: kettle
13
+
14
+ | piid | name | format | access | range / values | unit |
15
+ |---|---|---|---|---|---|
16
+ | 1 | status | uint8 | r/n | 0 Idle, 1 Heating, 2 Boiling, 3 Cooling down, 4 Keep warm | |
17
+ | 2 | fault | uint8 | r/n | 0–255 (0 = none) | |
18
+ | 3 | temperature | int8 | r/n | -30–100 | °C |
19
+ | 4 | target-temperature | uint8 | r/w/n | 40–99 | °C |
20
+ | 5 | auto-keep-warm | bool | r/w/n | | |
21
+ | 6 | keep-warm-temperature | uint8 | r/w/n | 0–100 | °C |
22
+ | 7 | on | bool | r/w/n | | |
23
+
24
+ ## siid 3: function (xiaomi-spec)
25
+
26
+ | piid | name | format | access | range / values | unit |
27
+ |---|---|---|---|---|---|
28
+ | 1 | keep-warm-time | uint16 | r/w/n | **60–1440** | min |
29
+ | 2 | custom-knob-temp | bool | r/w/n | | |
30
+ | 4 | lift-remember-temp | bool | r/w/n | | |
31
+ | 5 | boiling-reminder | bool | r/w/n | | |
32
+ | 6 | keep-warm-reminder | bool | r/w/n | | |
33
+ | 7 | kettle-lifting | bool | r/n | true = kettle lifted off base | |
34
+ | 8 | extended-mode | string | r/w/n | `name,temp,keep,keep_temp,minutes_...` presets | |
35
+ | 10 | warming-time | uint16 | r/n | 0–720 | min |
36
+ | 11 | target-mode | uint8 | r/w/n | 0 Heating, 1 Boiling, 10–15 Extended 1–6 | |
37
+ | 12 | heat-mode | string | r/w/n | `"<target>,<keep 1/0>,<keep_temp>,<keep_min>"`, e.g. `70,1,45,1440` | |
38
+ | 13 | boil-mode | string | r/w/n | same layout, e.g. `99,1,45,1440` | |
39
+
40
+ Action: **siid 3, aiid 1 `stop-work`** (in: `[]`, out: `[]`).
41
+
42
+ Other services (not used): siid 6 no-disturb (piid 1, bool); siid 4 knob-setting (piid 1–7, strings);
43
+ siid 5 local-timing (piid 1–6 strings, action aiid 1 push-water-reminder). Events omitted.
44
+
45
+ There is **no water-level property** in this spec. The closest safety signals are `fault` (2,2),
46
+ `status` (2,1) and `kettle-lifting` (3,7). The unofficial HA integration for this model reports that
47
+ fault `E2` = low water, but I have not confirmed the numeric fault code for it.
48
+
49
+ ## Proposed command mapping
50
+
51
+ How the device is *started* is not defined by the spec itself; the batch below comes from the community integration
52
+ [username-andrey/xiaomi-smart-kettle-ha](https://github.com/username-andrey/xiaomi-smart-kettle-ha)
53
+ (tested on firmware v2.2.7_0016), which sends one `set_properties` with these items in this order.
54
+ `boil` was confirmed on our hardware (starts heating without writing `on`).
55
+
56
+ | command | set_properties (siid,piid = value) |
57
+ |---|---|
58
+ | `boil` | (2,4)=99, (2,5)=false, (2,6)=keep_temp, (3,1)=keep_min, (3,11)=1, (3,13)=`"99,0,<keep_temp>,<keep_min>"` |
59
+ | `warm-to T` | (2,4)=T, (2,5)=false, (2,6)=keep_temp, (3,1)=keep_min, (3,11)=0, (3,12)=`"T,0,<keep_temp>,<keep_min>"` |
60
+ | `warm-keep T M` | (2,4)=T, (2,5)=true, (2,6)=T, (3,1)=M, (3,11)=0, (3,12)=`"T,1,T,M"` |
61
+ | `cancel` | action (3,1) stop-work |
62
+ | `status` | get (2,1) (2,2) (2,3) (2,4) (2,5) (2,6) (3,7) (3,10) (3,11) |
63
+
64
+ Validation limits taken from the spec: target 40–99 °C; keep-warm minutes 60–1440 (spec minimum is
65
+ 60, so `warm-keep` rejects less than 1 h).
66
+
67
+ Open points:
68
+ Which `fault` value means low water is still unknown.
@@ -0,0 +1,40 @@
1
+ [build-system]
2
+ requires = ["setuptools>=77"]
3
+ build-backend = "setuptools.build_meta"
4
+
5
+ [project]
6
+ name = "kettle-x2p-cli"
7
+ dynamic = ["version"]
8
+ description = "Local control of the Xiaomi Smart Kettle 2 Pro over your Wi-Fi, no cloud at runtime."
9
+ authors = [
10
+ { name = "Ilia Petrov-Komotskii", email = "ilia@inkedkettle.art" }
11
+ ]
12
+ license = "MIT"
13
+ readme = "README.md"
14
+ requires-python = ">=3.9"
15
+ keywords = ["xiaomi", "mijia", "miot", "miio", "kettle", "smart-home", "home-assistant", "cli"]
16
+ classifiers = [
17
+ "Programming Language :: Python :: 3",
18
+ "Operating System :: OS Independent",
19
+ "Environment :: Console",
20
+ "Topic :: Home Automation",
21
+ ]
22
+ dependencies = []
23
+
24
+ [project.urls]
25
+ Homepage = "https://github.com/ManeFunction/kettle-x2p-cli"
26
+ Repository = "https://github.com/ManeFunction/kettle-x2p-cli"
27
+ Issues = "https://github.com/ManeFunction/kettle-x2p-cli/issues"
28
+ Changelog = "https://github.com/ManeFunction/kettle-x2p-cli/blob/main/CHANGELOG.md"
29
+
30
+ [project.scripts]
31
+ kettle = "kettle.cli:main"
32
+
33
+ [tool.setuptools.dynamic]
34
+ version = { attr = "kettle.version" }
35
+
36
+ [tool.setuptools.packages.find]
37
+ where = ["src"]
38
+
39
+ [tool.setuptools.package-dir]
40
+ "" = "src"
@@ -0,0 +1,4 @@
1
+ [egg_info]
2
+ tag_build =
3
+ tag_date = 0
4
+
@@ -0,0 +1,3 @@
1
+ """Local control of the Xiaomi Smart Kettle 2 Pro over the LAN."""
2
+
3
+ version = "1.0.0"
@@ -0,0 +1,4 @@
1
+ from .cli import main
2
+
3
+ if __name__ == "__main__":
4
+ main()