thuhome-alert 0.1.0__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- thuhome_alert-0.1.0/.gitignore +22 -0
- thuhome_alert-0.1.0/CHANGELOG.md +57 -0
- thuhome_alert-0.1.0/LICENSE +2 -0
- thuhome_alert-0.1.0/PKG-INFO +318 -0
- thuhome_alert-0.1.0/README.md +279 -0
- thuhome_alert-0.1.0/pyproject.toml +98 -0
- thuhome_alert-0.1.0/src/thuhome_alert/__init__.py +34 -0
- thuhome_alert-0.1.0/src/thuhome_alert/__main__.py +8 -0
- thuhome_alert-0.1.0/src/thuhome_alert/alert.py +57 -0
- thuhome_alert-0.1.0/src/thuhome_alert/cli.py +197 -0
- thuhome_alert-0.1.0/src/thuhome_alert/config.py +398 -0
- thuhome_alert-0.1.0/src/thuhome_alert/login.py +96 -0
- thuhome_alert-0.1.0/src/thuhome_alert/monitor.py +214 -0
- thuhome_alert-0.1.0/src/thuhome_alert/paths.py +58 -0
- thuhome_alert-0.1.0/src/thuhome_alert/schedule.py +150 -0
- thuhome_alert-0.1.0/src/thuhome_alert/scrape.py +107 -0
- thuhome_alert-0.1.0/tests/conftest.py +51 -0
- thuhome_alert-0.1.0/tests/test_alert.py +100 -0
- thuhome_alert-0.1.0/tests/test_cli.py +240 -0
- thuhome_alert-0.1.0/tests/test_config.py +396 -0
- thuhome_alert-0.1.0/tests/test_login.py +148 -0
- thuhome_alert-0.1.0/tests/test_monitor.py +425 -0
- thuhome_alert-0.1.0/tests/test_paths.py +70 -0
- thuhome_alert-0.1.0/tests/test_schedule.py +193 -0
- thuhome_alert-0.1.0/tests/test_scrape.py +174 -0
|
@@ -0,0 +1,57 @@
|
|
|
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/en/1.1.0/),
|
|
6
|
+
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
|
|
7
|
+
|
|
8
|
+
## [Unreleased]
|
|
9
|
+
|
|
10
|
+
## [0.1.0] - 2026-08-10
|
|
11
|
+
|
|
12
|
+
### Added
|
|
13
|
+
|
|
14
|
+
- Initial Python port of the R `thuhomeAlert` package.
|
|
15
|
+
- `Account` dataclass with CSV I/O matching the R `account.csv` schema
|
|
16
|
+
byte-for-byte (zero-friction migration for existing R users).
|
|
17
|
+
- `setup()` function mirroring R's `setup()` — interactive prompts via
|
|
18
|
+
stdlib `getpass`, `force` / `update` semantics, `test_email` round-trip.
|
|
19
|
+
- `login()` — ASP.NET form submission against `myhome.tsinghua.edu.cn`
|
|
20
|
+
with `noLogin` redirect-based success detection.
|
|
21
|
+
- `scrape_balance()` — extracts water (`元` suffix) and electricity
|
|
22
|
+
balances via `#Netweb_Home_*_DetailCtrl1_lblele` DOM IDs.
|
|
23
|
+
- `send_alert()` — SMTP email via stdlib `smtplib` (replaces R's
|
|
24
|
+
`mailR`/rJava dependency). Hardcoded to `smtp.qq.com:587` to match R.
|
|
25
|
+
- `run_monitor()` — full orchestrator: login → scrape → record → alert →
|
|
26
|
+
periodic report. Auto-disables thresholds when portal returns no
|
|
27
|
+
balance for a utility.
|
|
28
|
+
- `schedule_daily()` — **unified** scheduler that auto-detects OS and
|
|
29
|
+
installs via `crontab` (POSIX) or `schtasks.exe` (Windows). Replaces
|
|
30
|
+
the R package's separate `schedule_daily()` / `schedule_daily_win()`.
|
|
31
|
+
- `thuhome-alert` CLI with `setup`, `run`, `schedule`, `migrate`
|
|
32
|
+
subcommands. Console script entry point: `pip install thuhome-alert`.
|
|
33
|
+
- XDG-compliant default paths (`$XDG_CONFIG_HOME/thuhomeAlert/account.csv`,
|
|
34
|
+
`$XDG_DATA_HOME/thuhomeAlert/stat.dorm.csv`).
|
|
35
|
+
- Full test suite (106 tests) with `responses` (HTTP), `pytest-mock`
|
|
36
|
+
(subprocess/SMTP), and `pytest-cov` — runs fully offline, fixes the
|
|
37
|
+
R tests' anti-patterns (real HTTP login, never-called `scrape_balance`).
|
|
38
|
+
- 91% line coverage.
|
|
39
|
+
- CI via GitHub Actions: matrix on Python 3.12/3.13 × Linux/macOS/Windows;
|
|
40
|
+
ruff lint + format check, mypy --strict, pytest with coverage.
|
|
41
|
+
- Release workflow using PyPI Trusted Publisher (OIDC, no API tokens).
|
|
42
|
+
|
|
43
|
+
### Changed
|
|
44
|
+
|
|
45
|
+
- N/A (initial release).
|
|
46
|
+
|
|
47
|
+
### Notes
|
|
48
|
+
|
|
49
|
+
- This is a sibling implementation to the R package, which remains
|
|
50
|
+
maintained in parallel at
|
|
51
|
+
[bill0628/thuhome-alert](https://github.com/bill0628/thuhome-alert).
|
|
52
|
+
Both share the same `account.csv` schema so users can switch freely.
|
|
53
|
+
- Runtime dependencies: `requests`, `beautifulsoup4` only (SMTP, cron,
|
|
54
|
+
paths, dates all via stdlib). Down from R's 8 imports + `rJava` JVM.
|
|
55
|
+
|
|
56
|
+
[Unreleased]: https://github.com/bill0628/thuhome-alert-py/compare/v0.1.0...HEAD
|
|
57
|
+
[0.1.0]: https://github.com/bill0628/thuhome-alert-py/releases/tag/v0.1.0
|
|
@@ -0,0 +1,318 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: thuhome-alert
|
|
3
|
+
Version: 0.1.0
|
|
4
|
+
Summary: Monitor water and electricity balance on Tsinghua University's myhome portal (myhome.tsinghua.edu.cn) and send email alerts when balances fall below user-defined thresholds.
|
|
5
|
+
Project-URL: Homepage, https://github.com/bill0628/thuhome-alert-py
|
|
6
|
+
Project-URL: Repository, https://github.com/bill0628/thuhome-alert-py
|
|
7
|
+
Project-URL: Issues, https://github.com/bill0628/thuhome-alert-py/issues
|
|
8
|
+
Project-URL: Changelog, https://github.com/bill0628/thuhome-alert-py/blob/main/CHANGELOG.md
|
|
9
|
+
Author-email: Bill Gao <gaobill@foxmail.com>
|
|
10
|
+
License-Expression: MIT
|
|
11
|
+
License-File: LICENSE
|
|
12
|
+
Keywords: alert,balance,dorm,myhome,tsinghua,utilities
|
|
13
|
+
Classifier: Development Status :: 4 - Beta
|
|
14
|
+
Classifier: Environment :: Console
|
|
15
|
+
Classifier: Intended Audience :: End Users/Desktop
|
|
16
|
+
Classifier: License :: OSI Approved :: MIT License
|
|
17
|
+
Classifier: Operating System :: OS Independent
|
|
18
|
+
Classifier: Programming Language :: Python :: 3
|
|
19
|
+
Classifier: Programming Language :: Python :: 3.12
|
|
20
|
+
Classifier: Programming Language :: Python :: 3.13
|
|
21
|
+
Classifier: Topic :: Communications :: Email
|
|
22
|
+
Classifier: Topic :: Internet :: WWW/HTTP :: Browsers
|
|
23
|
+
Classifier: Topic :: System :: Monitoring
|
|
24
|
+
Classifier: Typing :: Typed
|
|
25
|
+
Requires-Python: >=3.12
|
|
26
|
+
Requires-Dist: beautifulsoup4>=4.11
|
|
27
|
+
Requires-Dist: requests>=2.28
|
|
28
|
+
Provides-Extra: dev
|
|
29
|
+
Requires-Dist: build>=1.2; extra == 'dev'
|
|
30
|
+
Requires-Dist: mypy>=1.10; extra == 'dev'
|
|
31
|
+
Requires-Dist: pytest-cov>=5.0; extra == 'dev'
|
|
32
|
+
Requires-Dist: pytest-mock>=3.12; extra == 'dev'
|
|
33
|
+
Requires-Dist: pytest>=8.0; extra == 'dev'
|
|
34
|
+
Requires-Dist: responses>=0.24; extra == 'dev'
|
|
35
|
+
Requires-Dist: ruff>=0.5; extra == 'dev'
|
|
36
|
+
Requires-Dist: types-beautifulsoup4>=4.11; extra == 'dev'
|
|
37
|
+
Requires-Dist: types-requests>=2.28; extra == 'dev'
|
|
38
|
+
Description-Content-Type: text/markdown
|
|
39
|
+
|
|
40
|
+
# thuhome-alert
|
|
41
|
+
|
|
42
|
+
Monitor water and electricity balance on Tsinghua University's myhome portal
|
|
43
|
+
(`myhome.tsinghua.edu.cn`) and send email alerts when balances fall below
|
|
44
|
+
user-defined thresholds.
|
|
45
|
+
|
|
46
|
+
Python port of the R [`thuhomeAlert`](https://github.com/bill0628/thuhome-alert)
|
|
47
|
+
package. The two share the same `account.csv` schema — existing R users can
|
|
48
|
+
`pip install thuhome-alert` and keep their current config without migration.
|
|
49
|
+
|
|
50
|
+
## Features
|
|
51
|
+
|
|
52
|
+
- Login to Tsinghua myhome portal
|
|
53
|
+
- Fetch water and electricity balances
|
|
54
|
+
- Support monitoring water-only or electricity-only (for single-account users)
|
|
55
|
+
- Customizable threshold alerts
|
|
56
|
+
- Local history storage (append-only CSV)
|
|
57
|
+
- Schedule daily checks (Linux/macOS `crontab`, Windows Task Scheduler) via a
|
|
58
|
+
single unified `schedule` command that auto-detects the OS
|
|
59
|
+
- CLI: `thuhome-alert setup | run | schedule | migrate`
|
|
60
|
+
- **Two runtime dependencies only** — `requests` and `beautifulsoup4`. SMTP,
|
|
61
|
+
cron, paths, dates all via Python stdlib.
|
|
62
|
+
|
|
63
|
+
## Installation
|
|
64
|
+
|
|
65
|
+
```bash
|
|
66
|
+
pip install thuhome-alert
|
|
67
|
+
```
|
|
68
|
+
|
|
69
|
+
Or with [`pipx`](https://pypa.github.io/pipx/) for an isolated install:
|
|
70
|
+
|
|
71
|
+
```bash
|
|
72
|
+
pipx install thuhome-alert
|
|
73
|
+
```
|
|
74
|
+
|
|
75
|
+
Requires Python 3.12+.
|
|
76
|
+
|
|
77
|
+
## Quick Start
|
|
78
|
+
|
|
79
|
+
### 1. Configure Account
|
|
80
|
+
|
|
81
|
+
```bash
|
|
82
|
+
thuhome-alert setup \
|
|
83
|
+
--user your_username \
|
|
84
|
+
--passwd your_password \
|
|
85
|
+
--sender sender@qq.com \
|
|
86
|
+
--token your_smtp_token \
|
|
87
|
+
--recipient recipient@email.com \
|
|
88
|
+
--water 20 \
|
|
89
|
+
--electr 20 \
|
|
90
|
+
--no-test-email
|
|
91
|
+
```
|
|
92
|
+
|
|
93
|
+
Or from Python:
|
|
94
|
+
|
|
95
|
+
```python
|
|
96
|
+
import thuhome_alert
|
|
97
|
+
|
|
98
|
+
# Monitor both water and electricity
|
|
99
|
+
thuhome_alert.setup(
|
|
100
|
+
user_name="your_username",
|
|
101
|
+
user_pswd="your_password",
|
|
102
|
+
email_sender="sender@qq.com",
|
|
103
|
+
sender_token="your_smtp_token",
|
|
104
|
+
email_recipient="recipient@email.com",
|
|
105
|
+
water_threshold=20,
|
|
106
|
+
electr_threshold=20,
|
|
107
|
+
test_email=False,
|
|
108
|
+
)
|
|
109
|
+
|
|
110
|
+
# Monitor electricity only (set water_threshold = None)
|
|
111
|
+
thuhome_alert.setup(
|
|
112
|
+
user_name="your_username",
|
|
113
|
+
user_pswd="your_password",
|
|
114
|
+
email_sender="sender@qq.com",
|
|
115
|
+
sender_token="your_smtp_token",
|
|
116
|
+
email_recipient="recipient@email.com",
|
|
117
|
+
water_threshold=None,
|
|
118
|
+
electr_threshold=20,
|
|
119
|
+
test_email=False,
|
|
120
|
+
)
|
|
121
|
+
|
|
122
|
+
# Monitor water only (set electr_threshold = None)
|
|
123
|
+
thuhome_alert.setup(
|
|
124
|
+
user_name="your_username",
|
|
125
|
+
user_pswd="your_password",
|
|
126
|
+
email_sender="sender@qq.com",
|
|
127
|
+
sender_token="your_smtp_token",
|
|
128
|
+
email_recipient="recipient@email.com",
|
|
129
|
+
water_threshold=20,
|
|
130
|
+
electr_threshold=None,
|
|
131
|
+
test_email=False,
|
|
132
|
+
)
|
|
133
|
+
```
|
|
134
|
+
|
|
135
|
+
Config is saved to `$XDG_CONFIG_HOME/thuhomeAlert/account.csv` (default
|
|
136
|
+
`~/.config/thuhomeAlert/account.csv`, XDG-compliant). Balance history is
|
|
137
|
+
stored at `$XDG_DATA_HOME/thuhomeAlert/stat.dorm.csv` (default
|
|
138
|
+
`~/.local/share/thuhomeAlert/stat.dorm.csv`).
|
|
139
|
+
|
|
140
|
+
### 2. Run Monitor
|
|
141
|
+
|
|
142
|
+
```bash
|
|
143
|
+
# Check balances and send alerts if needed
|
|
144
|
+
thuhome-alert run
|
|
145
|
+
|
|
146
|
+
# Check only (no recording, no alerts)
|
|
147
|
+
thuhome-alert run --no-record --no-alert
|
|
148
|
+
```
|
|
149
|
+
|
|
150
|
+
Or from Python:
|
|
151
|
+
|
|
152
|
+
```python
|
|
153
|
+
import thuhome_alert
|
|
154
|
+
|
|
155
|
+
# Check balances and send alerts if needed
|
|
156
|
+
thuhome_alert.run_monitor()
|
|
157
|
+
|
|
158
|
+
# Check only (no recording, no alerts)
|
|
159
|
+
thuhome_alert.run_monitor(record=False, alert=False)
|
|
160
|
+
```
|
|
161
|
+
|
|
162
|
+
### 3. Schedule Daily Checks
|
|
163
|
+
|
|
164
|
+
```bash
|
|
165
|
+
# Run daily at 8 AM (auto-detects OS: crontab on Linux/macOS, schtasks on Windows)
|
|
166
|
+
thuhome-alert schedule --time 08:00
|
|
167
|
+
|
|
168
|
+
# Run daily at 6 PM
|
|
169
|
+
thuhome-alert schedule --time 18:00
|
|
170
|
+
|
|
171
|
+
# Uninstall the scheduled job
|
|
172
|
+
thuhome-alert schedule --uninstall
|
|
173
|
+
```
|
|
174
|
+
|
|
175
|
+
Or from Python:
|
|
176
|
+
|
|
177
|
+
```python
|
|
178
|
+
import thuhome_alert
|
|
179
|
+
|
|
180
|
+
# Run daily at 8 AM
|
|
181
|
+
thuhome_alert.schedule_daily("08:00")
|
|
182
|
+
|
|
183
|
+
# Run daily at 6 PM
|
|
184
|
+
thuhome_alert.schedule_daily("18:00")
|
|
185
|
+
```
|
|
186
|
+
|
|
187
|
+
### 4. Migrate (no-op for compatibility)
|
|
188
|
+
|
|
189
|
+
```bash
|
|
190
|
+
thuhome-alert migrate
|
|
191
|
+
```
|
|
192
|
+
|
|
193
|
+
The config schema is already compatible with this version, so this command
|
|
194
|
+
is a no-op that simply reports the config/data paths. It exists to provide
|
|
195
|
+
a forward-compatible migration hook for future versions.
|
|
196
|
+
|
|
197
|
+
## Configuration
|
|
198
|
+
|
|
199
|
+
The `account.csv` file (CSV format) has these columns — names match the R
|
|
200
|
+
package exactly, including the non-obvious `passwd` and plural `recipients`:
|
|
201
|
+
|
|
202
|
+
| column | meaning |
|
|
203
|
+
|--------------------|-------------------------------------------|
|
|
204
|
+
| `user` | portal username |
|
|
205
|
+
| `passwd` | portal password (note: not `password`) |
|
|
206
|
+
| `sender` | SMTP sender email |
|
|
207
|
+
| `recipients` | alert recipient email (note: plural) |
|
|
208
|
+
| `token` | SMTP password/token |
|
|
209
|
+
| `water_threshold` | RMB; empty disables water monitoring |
|
|
210
|
+
| `electr_threshold` | kWh; empty disables electricity monitoring |
|
|
211
|
+
| `report_freq` | `none` / `weekly` / `monthly` |
|
|
212
|
+
| `last_report` | ISO date string; updated after each report |
|
|
213
|
+
|
|
214
|
+
### Behavior notes
|
|
215
|
+
|
|
216
|
+
- `setup(update=True)` merges into existing config (only overwrites provided
|
|
217
|
+
fields); `setup(force=True)` overwrites the whole file. With neither, an
|
|
218
|
+
existing config is left untouched.
|
|
219
|
+
- `setup()` is **interactive** — it prompts via `input()` / `getpass.getpass()`
|
|
220
|
+
for any missing param. In non-interactive contexts (cron, tests, agents)
|
|
221
|
+
pass every param explicitly.
|
|
222
|
+
- `run_monitor()` will **auto-disable** a threshold by writing `None` back to
|
|
223
|
+
`account.csv` if the portal returns no balance for that utility (e.g. user
|
|
224
|
+
has no water account). Both thresholds `None` triggers a `RuntimeError`.
|
|
225
|
+
- `report_freq = "weekly"` fires when ≥7 days since `last_report`; `"monthly"`
|
|
226
|
+
fires when the calendar month/year changes. After sending, `last_report`
|
|
227
|
+
is overwritten in `account.csv`.
|
|
228
|
+
|
|
229
|
+
## SMTP limitation
|
|
230
|
+
|
|
231
|
+
`send_alert()` is **hardcoded to `smtp.qq.com:587`** — only works with a QQ
|
|
232
|
+
Mail sender account. This mirrors the R package's behavior (per its
|
|
233
|
+
`AGENTS.md`) and is intentionally not parameterized. Do not try to change
|
|
234
|
+
this without explicit coordination — the limitation is documented as a known
|
|
235
|
+
issue in both implementations.
|
|
236
|
+
|
|
237
|
+
## Dependencies
|
|
238
|
+
|
|
239
|
+
- Python >= 3.12
|
|
240
|
+
- `requests` (HTTP client)
|
|
241
|
+
- `beautifulsoup4` (HTML parsing)
|
|
242
|
+
|
|
243
|
+
Optional (only for development):
|
|
244
|
+
|
|
245
|
+
- `pytest`, `pytest-cov`, `pytest-mock`, `responses` (testing)
|
|
246
|
+
- `ruff` (lint + format)
|
|
247
|
+
- `mypy` (typecheck)
|
|
248
|
+
- `build` (sdist/wheel building)
|
|
249
|
+
|
|
250
|
+
## Development
|
|
251
|
+
|
|
252
|
+
```bash
|
|
253
|
+
# Clone
|
|
254
|
+
git clone https://github.com/bill0628/thuhome-alert-py.git
|
|
255
|
+
cd thuhome-alert-py
|
|
256
|
+
|
|
257
|
+
# Install with dev dependencies (uses uv)
|
|
258
|
+
uv sync --extra dev
|
|
259
|
+
|
|
260
|
+
# Run tests
|
|
261
|
+
uv run pytest
|
|
262
|
+
|
|
263
|
+
# Lint
|
|
264
|
+
uv run ruff check src/ tests/
|
|
265
|
+
uv run ruff format --check src/ tests/
|
|
266
|
+
|
|
267
|
+
# Typecheck
|
|
268
|
+
uv run mypy --strict src/thuhome_alert
|
|
269
|
+
|
|
270
|
+
# Build sdist + wheel
|
|
271
|
+
uv build
|
|
272
|
+
```
|
|
273
|
+
|
|
274
|
+
## Comparison with R package
|
|
275
|
+
|
|
276
|
+
| Aspect | R `thuhomeAlert` | Python `thuhome-alert` |
|
|
277
|
+
|---|---|---|
|
|
278
|
+
| Language | R >= 4.1 | Python >= 3.12 |
|
|
279
|
+
| Runtime deps | 8 packages + `rJava` JVM | 2 packages (`requests`, `beautifulsoup4`) |
|
|
280
|
+
| SMTP | `mailR` (requires JVM) | stdlib `smtplib` |
|
|
281
|
+
| Cron scheduling | `cronR` (Linux/mac) + `taskscheduleR` (Windows), separate functions | stdlib `subprocess` calling `crontab` / `schtasks`, **single unified** `schedule_daily()` |
|
|
282
|
+
| Interactive prompts | `readline()` / `getPass::getPass()` | stdlib `input()` / `getpass.getpass()` |
|
|
283
|
+
| Data frames | `tibble` / `dplyr` | `dataclass(frozen=True)` |
|
|
284
|
+
| Cold start | ~1-2s (R + tidyverse load) | ~150ms |
|
|
285
|
+
| `account.csv` schema | (canonical) | byte-for-byte identical |
|
|
286
|
+
| CLI | none (R functions only) | `thuhome-alert` console script |
|
|
287
|
+
|
|
288
|
+
The two implementations are designed to coexist: users can switch freely
|
|
289
|
+
between them since the config and data files are interchangeable.
|
|
290
|
+
|
|
291
|
+
## Scraping brittleness
|
|
292
|
+
|
|
293
|
+
The portal scraping depends on ASP.NET form field names and DOM element IDs
|
|
294
|
+
(mirrored exactly from the R implementation):
|
|
295
|
+
|
|
296
|
+
- Login form fields: `net_Default_LoginCtrl1$txtUserName`,
|
|
297
|
+
`net_Default_LoginCtrl1$txtPassword`
|
|
298
|
+
- Login success is detected by absence of `noLogin` in the redirect URL.
|
|
299
|
+
- Balance elements:
|
|
300
|
+
- `#Netweb_Home_water_DetailCtrl1_lblele` (water, suffix `元`)
|
|
301
|
+
- `#Netweb_Home_electricity_DetailCtrl1_lblele` (electricity, numeric)
|
|
302
|
+
|
|
303
|
+
If scraping breaks, these selectors are the first thing to verify against the
|
|
304
|
+
live portal HTML.
|
|
305
|
+
|
|
306
|
+
## License
|
|
307
|
+
|
|
308
|
+
MIT — see [LICENSE](LICENSE).
|
|
309
|
+
|
|
310
|
+
## Contributing
|
|
311
|
+
|
|
312
|
+
Issues and Pull Requests are welcome at
|
|
313
|
+
[github.com/bill0628/thuhome-alert-py](https://github.com/bill0628/thuhome-alert-py).
|
|
314
|
+
|
|
315
|
+
## See also
|
|
316
|
+
|
|
317
|
+
- [thuhome-alert (R package)](https://github.com/bill0628/thuhome-alert) —
|
|
318
|
+
the original R implementation, maintained in parallel.
|
|
@@ -0,0 +1,279 @@
|
|
|
1
|
+
# thuhome-alert
|
|
2
|
+
|
|
3
|
+
Monitor water and electricity balance on Tsinghua University's myhome portal
|
|
4
|
+
(`myhome.tsinghua.edu.cn`) and send email alerts when balances fall below
|
|
5
|
+
user-defined thresholds.
|
|
6
|
+
|
|
7
|
+
Python port of the R [`thuhomeAlert`](https://github.com/bill0628/thuhome-alert)
|
|
8
|
+
package. The two share the same `account.csv` schema — existing R users can
|
|
9
|
+
`pip install thuhome-alert` and keep their current config without migration.
|
|
10
|
+
|
|
11
|
+
## Features
|
|
12
|
+
|
|
13
|
+
- Login to Tsinghua myhome portal
|
|
14
|
+
- Fetch water and electricity balances
|
|
15
|
+
- Support monitoring water-only or electricity-only (for single-account users)
|
|
16
|
+
- Customizable threshold alerts
|
|
17
|
+
- Local history storage (append-only CSV)
|
|
18
|
+
- Schedule daily checks (Linux/macOS `crontab`, Windows Task Scheduler) via a
|
|
19
|
+
single unified `schedule` command that auto-detects the OS
|
|
20
|
+
- CLI: `thuhome-alert setup | run | schedule | migrate`
|
|
21
|
+
- **Two runtime dependencies only** — `requests` and `beautifulsoup4`. SMTP,
|
|
22
|
+
cron, paths, dates all via Python stdlib.
|
|
23
|
+
|
|
24
|
+
## Installation
|
|
25
|
+
|
|
26
|
+
```bash
|
|
27
|
+
pip install thuhome-alert
|
|
28
|
+
```
|
|
29
|
+
|
|
30
|
+
Or with [`pipx`](https://pypa.github.io/pipx/) for an isolated install:
|
|
31
|
+
|
|
32
|
+
```bash
|
|
33
|
+
pipx install thuhome-alert
|
|
34
|
+
```
|
|
35
|
+
|
|
36
|
+
Requires Python 3.12+.
|
|
37
|
+
|
|
38
|
+
## Quick Start
|
|
39
|
+
|
|
40
|
+
### 1. Configure Account
|
|
41
|
+
|
|
42
|
+
```bash
|
|
43
|
+
thuhome-alert setup \
|
|
44
|
+
--user your_username \
|
|
45
|
+
--passwd your_password \
|
|
46
|
+
--sender sender@qq.com \
|
|
47
|
+
--token your_smtp_token \
|
|
48
|
+
--recipient recipient@email.com \
|
|
49
|
+
--water 20 \
|
|
50
|
+
--electr 20 \
|
|
51
|
+
--no-test-email
|
|
52
|
+
```
|
|
53
|
+
|
|
54
|
+
Or from Python:
|
|
55
|
+
|
|
56
|
+
```python
|
|
57
|
+
import thuhome_alert
|
|
58
|
+
|
|
59
|
+
# Monitor both water and electricity
|
|
60
|
+
thuhome_alert.setup(
|
|
61
|
+
user_name="your_username",
|
|
62
|
+
user_pswd="your_password",
|
|
63
|
+
email_sender="sender@qq.com",
|
|
64
|
+
sender_token="your_smtp_token",
|
|
65
|
+
email_recipient="recipient@email.com",
|
|
66
|
+
water_threshold=20,
|
|
67
|
+
electr_threshold=20,
|
|
68
|
+
test_email=False,
|
|
69
|
+
)
|
|
70
|
+
|
|
71
|
+
# Monitor electricity only (set water_threshold = None)
|
|
72
|
+
thuhome_alert.setup(
|
|
73
|
+
user_name="your_username",
|
|
74
|
+
user_pswd="your_password",
|
|
75
|
+
email_sender="sender@qq.com",
|
|
76
|
+
sender_token="your_smtp_token",
|
|
77
|
+
email_recipient="recipient@email.com",
|
|
78
|
+
water_threshold=None,
|
|
79
|
+
electr_threshold=20,
|
|
80
|
+
test_email=False,
|
|
81
|
+
)
|
|
82
|
+
|
|
83
|
+
# Monitor water only (set electr_threshold = None)
|
|
84
|
+
thuhome_alert.setup(
|
|
85
|
+
user_name="your_username",
|
|
86
|
+
user_pswd="your_password",
|
|
87
|
+
email_sender="sender@qq.com",
|
|
88
|
+
sender_token="your_smtp_token",
|
|
89
|
+
email_recipient="recipient@email.com",
|
|
90
|
+
water_threshold=20,
|
|
91
|
+
electr_threshold=None,
|
|
92
|
+
test_email=False,
|
|
93
|
+
)
|
|
94
|
+
```
|
|
95
|
+
|
|
96
|
+
Config is saved to `$XDG_CONFIG_HOME/thuhomeAlert/account.csv` (default
|
|
97
|
+
`~/.config/thuhomeAlert/account.csv`, XDG-compliant). Balance history is
|
|
98
|
+
stored at `$XDG_DATA_HOME/thuhomeAlert/stat.dorm.csv` (default
|
|
99
|
+
`~/.local/share/thuhomeAlert/stat.dorm.csv`).
|
|
100
|
+
|
|
101
|
+
### 2. Run Monitor
|
|
102
|
+
|
|
103
|
+
```bash
|
|
104
|
+
# Check balances and send alerts if needed
|
|
105
|
+
thuhome-alert run
|
|
106
|
+
|
|
107
|
+
# Check only (no recording, no alerts)
|
|
108
|
+
thuhome-alert run --no-record --no-alert
|
|
109
|
+
```
|
|
110
|
+
|
|
111
|
+
Or from Python:
|
|
112
|
+
|
|
113
|
+
```python
|
|
114
|
+
import thuhome_alert
|
|
115
|
+
|
|
116
|
+
# Check balances and send alerts if needed
|
|
117
|
+
thuhome_alert.run_monitor()
|
|
118
|
+
|
|
119
|
+
# Check only (no recording, no alerts)
|
|
120
|
+
thuhome_alert.run_monitor(record=False, alert=False)
|
|
121
|
+
```
|
|
122
|
+
|
|
123
|
+
### 3. Schedule Daily Checks
|
|
124
|
+
|
|
125
|
+
```bash
|
|
126
|
+
# Run daily at 8 AM (auto-detects OS: crontab on Linux/macOS, schtasks on Windows)
|
|
127
|
+
thuhome-alert schedule --time 08:00
|
|
128
|
+
|
|
129
|
+
# Run daily at 6 PM
|
|
130
|
+
thuhome-alert schedule --time 18:00
|
|
131
|
+
|
|
132
|
+
# Uninstall the scheduled job
|
|
133
|
+
thuhome-alert schedule --uninstall
|
|
134
|
+
```
|
|
135
|
+
|
|
136
|
+
Or from Python:
|
|
137
|
+
|
|
138
|
+
```python
|
|
139
|
+
import thuhome_alert
|
|
140
|
+
|
|
141
|
+
# Run daily at 8 AM
|
|
142
|
+
thuhome_alert.schedule_daily("08:00")
|
|
143
|
+
|
|
144
|
+
# Run daily at 6 PM
|
|
145
|
+
thuhome_alert.schedule_daily("18:00")
|
|
146
|
+
```
|
|
147
|
+
|
|
148
|
+
### 4. Migrate (no-op for compatibility)
|
|
149
|
+
|
|
150
|
+
```bash
|
|
151
|
+
thuhome-alert migrate
|
|
152
|
+
```
|
|
153
|
+
|
|
154
|
+
The config schema is already compatible with this version, so this command
|
|
155
|
+
is a no-op that simply reports the config/data paths. It exists to provide
|
|
156
|
+
a forward-compatible migration hook for future versions.
|
|
157
|
+
|
|
158
|
+
## Configuration
|
|
159
|
+
|
|
160
|
+
The `account.csv` file (CSV format) has these columns — names match the R
|
|
161
|
+
package exactly, including the non-obvious `passwd` and plural `recipients`:
|
|
162
|
+
|
|
163
|
+
| column | meaning |
|
|
164
|
+
|--------------------|-------------------------------------------|
|
|
165
|
+
| `user` | portal username |
|
|
166
|
+
| `passwd` | portal password (note: not `password`) |
|
|
167
|
+
| `sender` | SMTP sender email |
|
|
168
|
+
| `recipients` | alert recipient email (note: plural) |
|
|
169
|
+
| `token` | SMTP password/token |
|
|
170
|
+
| `water_threshold` | RMB; empty disables water monitoring |
|
|
171
|
+
| `electr_threshold` | kWh; empty disables electricity monitoring |
|
|
172
|
+
| `report_freq` | `none` / `weekly` / `monthly` |
|
|
173
|
+
| `last_report` | ISO date string; updated after each report |
|
|
174
|
+
|
|
175
|
+
### Behavior notes
|
|
176
|
+
|
|
177
|
+
- `setup(update=True)` merges into existing config (only overwrites provided
|
|
178
|
+
fields); `setup(force=True)` overwrites the whole file. With neither, an
|
|
179
|
+
existing config is left untouched.
|
|
180
|
+
- `setup()` is **interactive** — it prompts via `input()` / `getpass.getpass()`
|
|
181
|
+
for any missing param. In non-interactive contexts (cron, tests, agents)
|
|
182
|
+
pass every param explicitly.
|
|
183
|
+
- `run_monitor()` will **auto-disable** a threshold by writing `None` back to
|
|
184
|
+
`account.csv` if the portal returns no balance for that utility (e.g. user
|
|
185
|
+
has no water account). Both thresholds `None` triggers a `RuntimeError`.
|
|
186
|
+
- `report_freq = "weekly"` fires when ≥7 days since `last_report`; `"monthly"`
|
|
187
|
+
fires when the calendar month/year changes. After sending, `last_report`
|
|
188
|
+
is overwritten in `account.csv`.
|
|
189
|
+
|
|
190
|
+
## SMTP limitation
|
|
191
|
+
|
|
192
|
+
`send_alert()` is **hardcoded to `smtp.qq.com:587`** — only works with a QQ
|
|
193
|
+
Mail sender account. This mirrors the R package's behavior (per its
|
|
194
|
+
`AGENTS.md`) and is intentionally not parameterized. Do not try to change
|
|
195
|
+
this without explicit coordination — the limitation is documented as a known
|
|
196
|
+
issue in both implementations.
|
|
197
|
+
|
|
198
|
+
## Dependencies
|
|
199
|
+
|
|
200
|
+
- Python >= 3.12
|
|
201
|
+
- `requests` (HTTP client)
|
|
202
|
+
- `beautifulsoup4` (HTML parsing)
|
|
203
|
+
|
|
204
|
+
Optional (only for development):
|
|
205
|
+
|
|
206
|
+
- `pytest`, `pytest-cov`, `pytest-mock`, `responses` (testing)
|
|
207
|
+
- `ruff` (lint + format)
|
|
208
|
+
- `mypy` (typecheck)
|
|
209
|
+
- `build` (sdist/wheel building)
|
|
210
|
+
|
|
211
|
+
## Development
|
|
212
|
+
|
|
213
|
+
```bash
|
|
214
|
+
# Clone
|
|
215
|
+
git clone https://github.com/bill0628/thuhome-alert-py.git
|
|
216
|
+
cd thuhome-alert-py
|
|
217
|
+
|
|
218
|
+
# Install with dev dependencies (uses uv)
|
|
219
|
+
uv sync --extra dev
|
|
220
|
+
|
|
221
|
+
# Run tests
|
|
222
|
+
uv run pytest
|
|
223
|
+
|
|
224
|
+
# Lint
|
|
225
|
+
uv run ruff check src/ tests/
|
|
226
|
+
uv run ruff format --check src/ tests/
|
|
227
|
+
|
|
228
|
+
# Typecheck
|
|
229
|
+
uv run mypy --strict src/thuhome_alert
|
|
230
|
+
|
|
231
|
+
# Build sdist + wheel
|
|
232
|
+
uv build
|
|
233
|
+
```
|
|
234
|
+
|
|
235
|
+
## Comparison with R package
|
|
236
|
+
|
|
237
|
+
| Aspect | R `thuhomeAlert` | Python `thuhome-alert` |
|
|
238
|
+
|---|---|---|
|
|
239
|
+
| Language | R >= 4.1 | Python >= 3.12 |
|
|
240
|
+
| Runtime deps | 8 packages + `rJava` JVM | 2 packages (`requests`, `beautifulsoup4`) |
|
|
241
|
+
| SMTP | `mailR` (requires JVM) | stdlib `smtplib` |
|
|
242
|
+
| Cron scheduling | `cronR` (Linux/mac) + `taskscheduleR` (Windows), separate functions | stdlib `subprocess` calling `crontab` / `schtasks`, **single unified** `schedule_daily()` |
|
|
243
|
+
| Interactive prompts | `readline()` / `getPass::getPass()` | stdlib `input()` / `getpass.getpass()` |
|
|
244
|
+
| Data frames | `tibble` / `dplyr` | `dataclass(frozen=True)` |
|
|
245
|
+
| Cold start | ~1-2s (R + tidyverse load) | ~150ms |
|
|
246
|
+
| `account.csv` schema | (canonical) | byte-for-byte identical |
|
|
247
|
+
| CLI | none (R functions only) | `thuhome-alert` console script |
|
|
248
|
+
|
|
249
|
+
The two implementations are designed to coexist: users can switch freely
|
|
250
|
+
between them since the config and data files are interchangeable.
|
|
251
|
+
|
|
252
|
+
## Scraping brittleness
|
|
253
|
+
|
|
254
|
+
The portal scraping depends on ASP.NET form field names and DOM element IDs
|
|
255
|
+
(mirrored exactly from the R implementation):
|
|
256
|
+
|
|
257
|
+
- Login form fields: `net_Default_LoginCtrl1$txtUserName`,
|
|
258
|
+
`net_Default_LoginCtrl1$txtPassword`
|
|
259
|
+
- Login success is detected by absence of `noLogin` in the redirect URL.
|
|
260
|
+
- Balance elements:
|
|
261
|
+
- `#Netweb_Home_water_DetailCtrl1_lblele` (water, suffix `元`)
|
|
262
|
+
- `#Netweb_Home_electricity_DetailCtrl1_lblele` (electricity, numeric)
|
|
263
|
+
|
|
264
|
+
If scraping breaks, these selectors are the first thing to verify against the
|
|
265
|
+
live portal HTML.
|
|
266
|
+
|
|
267
|
+
## License
|
|
268
|
+
|
|
269
|
+
MIT — see [LICENSE](LICENSE).
|
|
270
|
+
|
|
271
|
+
## Contributing
|
|
272
|
+
|
|
273
|
+
Issues and Pull Requests are welcome at
|
|
274
|
+
[github.com/bill0628/thuhome-alert-py](https://github.com/bill0628/thuhome-alert-py).
|
|
275
|
+
|
|
276
|
+
## See also
|
|
277
|
+
|
|
278
|
+
- [thuhome-alert (R package)](https://github.com/bill0628/thuhome-alert) —
|
|
279
|
+
the original R implementation, maintained in parallel.
|