aioleviton 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.
- aioleviton-0.1.0/LICENSE +21 -0
- aioleviton-0.1.0/PKG-INFO +180 -0
- aioleviton-0.1.0/README.md +158 -0
- aioleviton-0.1.0/aioleviton/__init__.py +33 -0
- aioleviton-0.1.0/aioleviton/client.py +627 -0
- aioleviton-0.1.0/aioleviton/const.py +38 -0
- aioleviton-0.1.0/aioleviton/exceptions.py +25 -0
- aioleviton-0.1.0/aioleviton/models.py +460 -0
- aioleviton-0.1.0/aioleviton/py.typed +0 -0
- aioleviton-0.1.0/aioleviton/websocket.py +294 -0
- aioleviton-0.1.0/aioleviton.egg-info/PKG-INFO +180 -0
- aioleviton-0.1.0/aioleviton.egg-info/SOURCES.txt +15 -0
- aioleviton-0.1.0/aioleviton.egg-info/dependency_links.txt +1 -0
- aioleviton-0.1.0/aioleviton.egg-info/requires.txt +1 -0
- aioleviton-0.1.0/aioleviton.egg-info/top_level.txt +1 -0
- aioleviton-0.1.0/pyproject.toml +50 -0
- aioleviton-0.1.0/setup.cfg +4 -0
aioleviton-0.1.0/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 gtxaspec
|
|
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,180 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: aioleviton
|
|
3
|
+
Version: 0.1.0
|
|
4
|
+
Summary: Async Python client for the Leviton My Leviton cloud API
|
|
5
|
+
Author: gtxaspec
|
|
6
|
+
License-Expression: MIT
|
|
7
|
+
Project-URL: Homepage, https://github.com/gtxaspec/aioleviton
|
|
8
|
+
Project-URL: Repository, https://github.com/gtxaspec/aioleviton
|
|
9
|
+
Project-URL: Issues, https://github.com/gtxaspec/aioleviton/issues
|
|
10
|
+
Classifier: Development Status :: 4 - Beta
|
|
11
|
+
Classifier: Framework :: AsyncIO
|
|
12
|
+
Classifier: Intended Audience :: Developers
|
|
13
|
+
Classifier: Programming Language :: Python :: 3
|
|
14
|
+
Classifier: Programming Language :: Python :: 3.12
|
|
15
|
+
Classifier: Programming Language :: Python :: 3.13
|
|
16
|
+
Classifier: Typing :: Typed
|
|
17
|
+
Requires-Python: >=3.12
|
|
18
|
+
Description-Content-Type: text/markdown
|
|
19
|
+
License-File: LICENSE
|
|
20
|
+
Requires-Dist: aiohttp>=3.9.0
|
|
21
|
+
Dynamic: license-file
|
|
22
|
+
|
|
23
|
+
# aioleviton
|
|
24
|
+
|
|
25
|
+
Async Python client for the Leviton My Leviton cloud API.
|
|
26
|
+
|
|
27
|
+
Supports LWHEM and DAU/LDATA Smart Load Centers with WebSocket real-time push and REST API fallback.
|
|
28
|
+
|
|
29
|
+
## Features
|
|
30
|
+
|
|
31
|
+
- Pure `asyncio` with `aiohttp` -- no blocking calls
|
|
32
|
+
- Accepts an injected `aiohttp.ClientSession` for connection pooling
|
|
33
|
+
- WebSocket real-time push notifications with automatic subscription management
|
|
34
|
+
- Full REST API coverage: authentication, device discovery, breaker control, energy history
|
|
35
|
+
- Typed data models with PEP 561 `py.typed` marker
|
|
36
|
+
- Support for both hub types: LWHEM (`IotWhem`) and DAU/LDATA (`ResidentialBreakerPanel`)
|
|
37
|
+
- Two-factor authentication (2FA) support
|
|
38
|
+
|
|
39
|
+
## Installation
|
|
40
|
+
|
|
41
|
+
```bash
|
|
42
|
+
pip install aioleviton
|
|
43
|
+
```
|
|
44
|
+
|
|
45
|
+
## Quick Start
|
|
46
|
+
|
|
47
|
+
```python
|
|
48
|
+
import aiohttp
|
|
49
|
+
from aioleviton import LevitonClient, LevitonWebSocket
|
|
50
|
+
|
|
51
|
+
async def main():
|
|
52
|
+
async with aiohttp.ClientSession() as session:
|
|
53
|
+
# Authenticate
|
|
54
|
+
client = LevitonClient(session)
|
|
55
|
+
auth = await client.login("user@example.com", "password")
|
|
56
|
+
|
|
57
|
+
# Discover devices
|
|
58
|
+
permissions = await client.get_permissions()
|
|
59
|
+
for perm in permissions:
|
|
60
|
+
if perm.residential_account_id:
|
|
61
|
+
residences = await client.get_residences(perm.residential_account_id)
|
|
62
|
+
for residence in residences:
|
|
63
|
+
whems = await client.get_whems(residence.id)
|
|
64
|
+
panels = await client.get_panels(residence.id)
|
|
65
|
+
|
|
66
|
+
# Get breakers for a LWHEM hub
|
|
67
|
+
for whem in whems:
|
|
68
|
+
breakers = await client.get_whem_breakers(whem.id)
|
|
69
|
+
cts = await client.get_cts(whem.id)
|
|
70
|
+
|
|
71
|
+
# Connect WebSocket for real-time updates
|
|
72
|
+
ws = LevitonWebSocket(
|
|
73
|
+
session=session,
|
|
74
|
+
token=auth.token,
|
|
75
|
+
user_id=auth.user_id,
|
|
76
|
+
user=auth.user,
|
|
77
|
+
token_created=auth.created,
|
|
78
|
+
token_ttl=auth.ttl,
|
|
79
|
+
)
|
|
80
|
+
await ws.connect()
|
|
81
|
+
|
|
82
|
+
# Subscribe to a hub (delivers all child breaker/CT updates)
|
|
83
|
+
await ws.subscribe("IotWhem", whem.id)
|
|
84
|
+
|
|
85
|
+
# Handle notifications
|
|
86
|
+
ws.on_notification(lambda data: print("Update:", data))
|
|
87
|
+
```
|
|
88
|
+
|
|
89
|
+
## Supported Devices
|
|
90
|
+
|
|
91
|
+
| Device | API Model | Hub Type |
|
|
92
|
+
|--------|-----------|----------|
|
|
93
|
+
| LWHEM (Whole Home Energy Module) | `IotWhem` | Wi-Fi hub |
|
|
94
|
+
| DAU / LDATA (Data Acquisition Unit) | `ResidentialBreakerPanel` | Wi-Fi hub |
|
|
95
|
+
| Smart Breaker Gen 1 (trip only) | `ResidentialBreaker` | Child of LWHEM or DAU |
|
|
96
|
+
| Smart Breaker Gen 2 (on/off) | `ResidentialBreaker` | Child of LWHEM or DAU |
|
|
97
|
+
| Current Transformer (CT) | `IotCt` | Child of LWHEM only |
|
|
98
|
+
| LSBMA Add-on CT | `ResidentialBreaker` | Virtual composite |
|
|
99
|
+
|
|
100
|
+
## Breaker Control
|
|
101
|
+
|
|
102
|
+
```python
|
|
103
|
+
# Trip a Gen 1 breaker (cannot turn back on remotely)
|
|
104
|
+
await client.trip_breaker(breaker_id)
|
|
105
|
+
|
|
106
|
+
# Turn on/off a Gen 2 breaker
|
|
107
|
+
await client.turn_on_breaker(breaker_id)
|
|
108
|
+
await client.turn_off_breaker(breaker_id)
|
|
109
|
+
|
|
110
|
+
# Blink LED on a breaker
|
|
111
|
+
await client.blink_led(breaker_id)
|
|
112
|
+
|
|
113
|
+
# Identify LED on a LWHEM hub
|
|
114
|
+
await client.identify_whem(whem_id)
|
|
115
|
+
```
|
|
116
|
+
|
|
117
|
+
## Energy History
|
|
118
|
+
|
|
119
|
+
Energy history endpoints return consumption data for all devices in a residence.
|
|
120
|
+
Data is keyed by hub ID, then by breaker position and CT channel.
|
|
121
|
+
|
|
122
|
+
```python
|
|
123
|
+
# Daily energy (hourly data points)
|
|
124
|
+
day = await client.get_energy_for_day(
|
|
125
|
+
residence_id=713744,
|
|
126
|
+
start_day="2026-02-16",
|
|
127
|
+
timezone="America/Los_Angeles",
|
|
128
|
+
)
|
|
129
|
+
|
|
130
|
+
# Weekly energy (daily data points for 7 days)
|
|
131
|
+
week = await client.get_energy_for_week(
|
|
132
|
+
residence_id=713744,
|
|
133
|
+
start_day="2026-02-17",
|
|
134
|
+
timezone="America/Los_Angeles",
|
|
135
|
+
)
|
|
136
|
+
|
|
137
|
+
# Monthly energy (daily data points for billing month)
|
|
138
|
+
month = await client.get_energy_for_month(
|
|
139
|
+
residence_id=713744,
|
|
140
|
+
billing_day_in_month="2026-02-28",
|
|
141
|
+
timezone="America/Los_Angeles",
|
|
142
|
+
)
|
|
143
|
+
|
|
144
|
+
# Yearly energy (monthly data points for 12 months)
|
|
145
|
+
year = await client.get_energy_for_year(
|
|
146
|
+
residence_id=713744,
|
|
147
|
+
billing_day_in_end_month="2026-02-16",
|
|
148
|
+
timezone="America/Los_Angeles",
|
|
149
|
+
)
|
|
150
|
+
|
|
151
|
+
# Response structure:
|
|
152
|
+
# {
|
|
153
|
+
# "<hub_id>": {
|
|
154
|
+
# "residentialBreakers": {"<position>": [{x, timestamp, energyConsumption, totalCost, ...}]},
|
|
155
|
+
# "iotCts": {"<channel>": [...]},
|
|
156
|
+
# "totals": [...]
|
|
157
|
+
# },
|
|
158
|
+
# "totals": [...] # residence-level totals
|
|
159
|
+
# }
|
|
160
|
+
```
|
|
161
|
+
|
|
162
|
+
## Firmware Check
|
|
163
|
+
|
|
164
|
+
```python
|
|
165
|
+
# Check for available firmware updates
|
|
166
|
+
firmware = await client.check_firmware(
|
|
167
|
+
app_id="LWHEM",
|
|
168
|
+
model="AZ",
|
|
169
|
+
serial="1000_003D_5D58",
|
|
170
|
+
model_type="IotWhem",
|
|
171
|
+
)
|
|
172
|
+
# Returns list of firmware objects with version, fileUrl, signature, hash, size, notes
|
|
173
|
+
for fw in firmware:
|
|
174
|
+
print(f"v{fw['version']}: {fw['fileUrl']}")
|
|
175
|
+
```
|
|
176
|
+
|
|
177
|
+
|
|
178
|
+
## License
|
|
179
|
+
|
|
180
|
+
MIT
|
|
@@ -0,0 +1,158 @@
|
|
|
1
|
+
# aioleviton
|
|
2
|
+
|
|
3
|
+
Async Python client for the Leviton My Leviton cloud API.
|
|
4
|
+
|
|
5
|
+
Supports LWHEM and DAU/LDATA Smart Load Centers with WebSocket real-time push and REST API fallback.
|
|
6
|
+
|
|
7
|
+
## Features
|
|
8
|
+
|
|
9
|
+
- Pure `asyncio` with `aiohttp` -- no blocking calls
|
|
10
|
+
- Accepts an injected `aiohttp.ClientSession` for connection pooling
|
|
11
|
+
- WebSocket real-time push notifications with automatic subscription management
|
|
12
|
+
- Full REST API coverage: authentication, device discovery, breaker control, energy history
|
|
13
|
+
- Typed data models with PEP 561 `py.typed` marker
|
|
14
|
+
- Support for both hub types: LWHEM (`IotWhem`) and DAU/LDATA (`ResidentialBreakerPanel`)
|
|
15
|
+
- Two-factor authentication (2FA) support
|
|
16
|
+
|
|
17
|
+
## Installation
|
|
18
|
+
|
|
19
|
+
```bash
|
|
20
|
+
pip install aioleviton
|
|
21
|
+
```
|
|
22
|
+
|
|
23
|
+
## Quick Start
|
|
24
|
+
|
|
25
|
+
```python
|
|
26
|
+
import aiohttp
|
|
27
|
+
from aioleviton import LevitonClient, LevitonWebSocket
|
|
28
|
+
|
|
29
|
+
async def main():
|
|
30
|
+
async with aiohttp.ClientSession() as session:
|
|
31
|
+
# Authenticate
|
|
32
|
+
client = LevitonClient(session)
|
|
33
|
+
auth = await client.login("user@example.com", "password")
|
|
34
|
+
|
|
35
|
+
# Discover devices
|
|
36
|
+
permissions = await client.get_permissions()
|
|
37
|
+
for perm in permissions:
|
|
38
|
+
if perm.residential_account_id:
|
|
39
|
+
residences = await client.get_residences(perm.residential_account_id)
|
|
40
|
+
for residence in residences:
|
|
41
|
+
whems = await client.get_whems(residence.id)
|
|
42
|
+
panels = await client.get_panels(residence.id)
|
|
43
|
+
|
|
44
|
+
# Get breakers for a LWHEM hub
|
|
45
|
+
for whem in whems:
|
|
46
|
+
breakers = await client.get_whem_breakers(whem.id)
|
|
47
|
+
cts = await client.get_cts(whem.id)
|
|
48
|
+
|
|
49
|
+
# Connect WebSocket for real-time updates
|
|
50
|
+
ws = LevitonWebSocket(
|
|
51
|
+
session=session,
|
|
52
|
+
token=auth.token,
|
|
53
|
+
user_id=auth.user_id,
|
|
54
|
+
user=auth.user,
|
|
55
|
+
token_created=auth.created,
|
|
56
|
+
token_ttl=auth.ttl,
|
|
57
|
+
)
|
|
58
|
+
await ws.connect()
|
|
59
|
+
|
|
60
|
+
# Subscribe to a hub (delivers all child breaker/CT updates)
|
|
61
|
+
await ws.subscribe("IotWhem", whem.id)
|
|
62
|
+
|
|
63
|
+
# Handle notifications
|
|
64
|
+
ws.on_notification(lambda data: print("Update:", data))
|
|
65
|
+
```
|
|
66
|
+
|
|
67
|
+
## Supported Devices
|
|
68
|
+
|
|
69
|
+
| Device | API Model | Hub Type |
|
|
70
|
+
|--------|-----------|----------|
|
|
71
|
+
| LWHEM (Whole Home Energy Module) | `IotWhem` | Wi-Fi hub |
|
|
72
|
+
| DAU / LDATA (Data Acquisition Unit) | `ResidentialBreakerPanel` | Wi-Fi hub |
|
|
73
|
+
| Smart Breaker Gen 1 (trip only) | `ResidentialBreaker` | Child of LWHEM or DAU |
|
|
74
|
+
| Smart Breaker Gen 2 (on/off) | `ResidentialBreaker` | Child of LWHEM or DAU |
|
|
75
|
+
| Current Transformer (CT) | `IotCt` | Child of LWHEM only |
|
|
76
|
+
| LSBMA Add-on CT | `ResidentialBreaker` | Virtual composite |
|
|
77
|
+
|
|
78
|
+
## Breaker Control
|
|
79
|
+
|
|
80
|
+
```python
|
|
81
|
+
# Trip a Gen 1 breaker (cannot turn back on remotely)
|
|
82
|
+
await client.trip_breaker(breaker_id)
|
|
83
|
+
|
|
84
|
+
# Turn on/off a Gen 2 breaker
|
|
85
|
+
await client.turn_on_breaker(breaker_id)
|
|
86
|
+
await client.turn_off_breaker(breaker_id)
|
|
87
|
+
|
|
88
|
+
# Blink LED on a breaker
|
|
89
|
+
await client.blink_led(breaker_id)
|
|
90
|
+
|
|
91
|
+
# Identify LED on a LWHEM hub
|
|
92
|
+
await client.identify_whem(whem_id)
|
|
93
|
+
```
|
|
94
|
+
|
|
95
|
+
## Energy History
|
|
96
|
+
|
|
97
|
+
Energy history endpoints return consumption data for all devices in a residence.
|
|
98
|
+
Data is keyed by hub ID, then by breaker position and CT channel.
|
|
99
|
+
|
|
100
|
+
```python
|
|
101
|
+
# Daily energy (hourly data points)
|
|
102
|
+
day = await client.get_energy_for_day(
|
|
103
|
+
residence_id=713744,
|
|
104
|
+
start_day="2026-02-16",
|
|
105
|
+
timezone="America/Los_Angeles",
|
|
106
|
+
)
|
|
107
|
+
|
|
108
|
+
# Weekly energy (daily data points for 7 days)
|
|
109
|
+
week = await client.get_energy_for_week(
|
|
110
|
+
residence_id=713744,
|
|
111
|
+
start_day="2026-02-17",
|
|
112
|
+
timezone="America/Los_Angeles",
|
|
113
|
+
)
|
|
114
|
+
|
|
115
|
+
# Monthly energy (daily data points for billing month)
|
|
116
|
+
month = await client.get_energy_for_month(
|
|
117
|
+
residence_id=713744,
|
|
118
|
+
billing_day_in_month="2026-02-28",
|
|
119
|
+
timezone="America/Los_Angeles",
|
|
120
|
+
)
|
|
121
|
+
|
|
122
|
+
# Yearly energy (monthly data points for 12 months)
|
|
123
|
+
year = await client.get_energy_for_year(
|
|
124
|
+
residence_id=713744,
|
|
125
|
+
billing_day_in_end_month="2026-02-16",
|
|
126
|
+
timezone="America/Los_Angeles",
|
|
127
|
+
)
|
|
128
|
+
|
|
129
|
+
# Response structure:
|
|
130
|
+
# {
|
|
131
|
+
# "<hub_id>": {
|
|
132
|
+
# "residentialBreakers": {"<position>": [{x, timestamp, energyConsumption, totalCost, ...}]},
|
|
133
|
+
# "iotCts": {"<channel>": [...]},
|
|
134
|
+
# "totals": [...]
|
|
135
|
+
# },
|
|
136
|
+
# "totals": [...] # residence-level totals
|
|
137
|
+
# }
|
|
138
|
+
```
|
|
139
|
+
|
|
140
|
+
## Firmware Check
|
|
141
|
+
|
|
142
|
+
```python
|
|
143
|
+
# Check for available firmware updates
|
|
144
|
+
firmware = await client.check_firmware(
|
|
145
|
+
app_id="LWHEM",
|
|
146
|
+
model="AZ",
|
|
147
|
+
serial="1000_003D_5D58",
|
|
148
|
+
model_type="IotWhem",
|
|
149
|
+
)
|
|
150
|
+
# Returns list of firmware objects with version, fileUrl, signature, hash, size, notes
|
|
151
|
+
for fw in firmware:
|
|
152
|
+
print(f"v{fw['version']}: {fw['fileUrl']}")
|
|
153
|
+
```
|
|
154
|
+
|
|
155
|
+
|
|
156
|
+
## License
|
|
157
|
+
|
|
158
|
+
MIT
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
"""Async Python client for the Leviton My Leviton cloud API."""
|
|
2
|
+
|
|
3
|
+
__version__ = "0.1.0"
|
|
4
|
+
|
|
5
|
+
from .client import LevitonClient
|
|
6
|
+
from .exceptions import (
|
|
7
|
+
LevitonAuthError,
|
|
8
|
+
LevitonConnectionError,
|
|
9
|
+
LevitonError,
|
|
10
|
+
LevitonInvalidCode,
|
|
11
|
+
LevitonTokenExpired,
|
|
12
|
+
LevitonTwoFactorRequired,
|
|
13
|
+
)
|
|
14
|
+
from .models import AuthToken, Breaker, Ct, Panel, Permission, Residence, Whem
|
|
15
|
+
from .websocket import LevitonWebSocket
|
|
16
|
+
|
|
17
|
+
__all__ = [
|
|
18
|
+
"AuthToken",
|
|
19
|
+
"Breaker",
|
|
20
|
+
"Ct",
|
|
21
|
+
"LevitonAuthError",
|
|
22
|
+
"LevitonClient",
|
|
23
|
+
"LevitonConnectionError",
|
|
24
|
+
"LevitonError",
|
|
25
|
+
"LevitonInvalidCode",
|
|
26
|
+
"LevitonTokenExpired",
|
|
27
|
+
"LevitonTwoFactorRequired",
|
|
28
|
+
"LevitonWebSocket",
|
|
29
|
+
"Panel",
|
|
30
|
+
"Permission",
|
|
31
|
+
"Residence",
|
|
32
|
+
"Whem",
|
|
33
|
+
]
|