A Python 3.11-compatible, aiohttp 3.10+ compatible client for the LightWaveRF Gen2 WebSocket API.
This is a community fork of the abandoned lightwave2 library, fixing several breaking issues that make it unusable on modern Python and aiohttp versions.
lightwave2 has not been updated since 2022. Running it on Python 3.11 with aiohttp ≥ 3.10 produces errors like:
RuntimeError: Timeout context manager should be used inside a task
AttributeError: 'NoneType' object has no attribute 'send_str'
asyncio.locks.Event object is bound to a different event loop
Commands appear to succeed (no exception raised to the caller) but devices do not respond. The root causes are:
@asyncio.coroutine/yield from— removed in Python 3.11; the consumer handler crashed silentlyasync_connect()infinite recursion — no retry limit; when called outside a Task context,asyncio.sleep()raisesRuntimeErrorin aiohttp 3.10+ which usesasyncio.timeout()internally- Consumer Task not started before authenticate —
_authenticate()blocks onasyncio.Event.wait()with nobody reading the socket, causing a 60-second hang _websocket.send_str()onNone— after a failed reconnect,_websocketisNone; callingsend_strraisesAttributeErrorrather than a clean error
| Issue | Fix |
|---|---|
@asyncio.coroutine / yield from |
Rewritten as async def / await throughout |
| Infinite reconnect loop | Iterative async_connect() with configurable max_tries (default 8); raises ConnectionError on exhaustion |
| Consumer not running during auth | Consumer Task created via asyncio.create_task() before _authenticate() |
send_str on None |
Guard in _async_sendmessage; raises ConnectionError instead of AttributeError |
| Feature model | _LWRFFeature objects with .id and ._state (replacing bare [id, value] lists) |
| Callback signature | callback(feature_name, feature_id, prev_value, new_value) |
Copy lightwave3.py into your project. No package installation required.
Or install directly from this repo:
pip install git+https://github.com/AbsenteeAtom/Lightwave3.gitThe API is a drop-in replacement for lightwave2.LWLink2 (WebSocket/local app API):
import lightwave3 as lightwave2 # or: import lightwave3
link = lightwave3.LWLink2(email, password)
await link.async_connect()
await link.async_get_hierarchy()
# Register a state-change callback
await link.async_register_callback(my_callback)
# my_callback(feature_name, feature_id, prev_value, new_value)
# Control devices
await link.async_turn_on_by_featureset_id(featureset_id)
await link.async_turn_off_by_featureset_id(featureset_id)
await link.async_set_brightness_by_featureset_id(featureset_id, level)
# Inspect featuresets
for fs_id, fs in link.featuresets.items():
for feat_name, feat in fs.features.items():
print(fs.name, feat_name, feat.id, feat._state)# lightwave3:
import lightwave3 as lightwave2
# lightwave2 (original):
from lightwave2 import lightwave2| Package | Version |
|---|---|
| Python | 3.11 |
| aiohttp | 3.13.3 |
| websockets | 16.0 |
LWLink2Public (the HTTP public API variant) is not implemented. This library covers the local WebSocket API (wss://v1-linkplus-app.lightwaverf.com) only.
MIT — same as the original lightwave2.