daybetter-services-python 1.0.6__tar.gz → 1.0.8__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.
- daybetter_services_python-1.0.8/PKG-INFO +127 -0
- daybetter_services_python-1.0.8/README.md +92 -0
- {daybetter_services_python-1.0.6 → daybetter_services_python-1.0.8}/daybetter_python/__init__.py +1 -1
- {daybetter_services_python-1.0.6 → daybetter_services_python-1.0.8}/daybetter_python/client.py +86 -92
- daybetter_services_python-1.0.8/daybetter_python/exceptions.py +38 -0
- daybetter_services_python-1.0.8/daybetter_python/py.typed +0 -0
- daybetter_services_python-1.0.8/daybetter_services_python.egg-info/PKG-INFO +127 -0
- {daybetter_services_python-1.0.6 → daybetter_services_python-1.0.8}/daybetter_services_python.egg-info/SOURCES.txt +3 -2
- {daybetter_services_python-1.0.6 → daybetter_services_python-1.0.8}/pyproject.toml +9 -1
- daybetter_services_python-1.0.8/tests/test_placeholder.py +7 -0
- daybetter_services_python-1.0.6/PKG-INFO +0 -98
- daybetter_services_python-1.0.6/README.md +0 -59
- daybetter_services_python-1.0.6/daybetter_python/exceptions.py +0 -13
- daybetter_services_python-1.0.6/daybetter_services_python.egg-info/PKG-INFO +0 -98
- daybetter_services_python-1.0.6/setup.py +0 -34
- {daybetter_services_python-1.0.6 → daybetter_services_python-1.0.8}/LICENSE +0 -0
- {daybetter_services_python-1.0.6 → daybetter_services_python-1.0.8}/daybetter_services_python.egg-info/dependency_links.txt +0 -0
- {daybetter_services_python-1.0.6 → daybetter_services_python-1.0.8}/daybetter_services_python.egg-info/requires.txt +0 -0
- {daybetter_services_python-1.0.6 → daybetter_services_python-1.0.8}/daybetter_services_python.egg-info/top_level.txt +0 -0
- {daybetter_services_python-1.0.6 → daybetter_services_python-1.0.8}/setup.cfg +0 -0
|
@@ -0,0 +1,127 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: daybetter-services-python
|
|
3
|
+
Version: 1.0.8
|
|
4
|
+
Summary: Python client for DayBetter devices and services
|
|
5
|
+
Author-email: THDayBetter <chenp2368@163.com>
|
|
6
|
+
Maintainer-email: THDayBetter <chenp2368@163.com>
|
|
7
|
+
License-Expression: MIT
|
|
8
|
+
Project-URL: Homepage, https://github.com/THDayBetter/daybetter-python
|
|
9
|
+
Project-URL: Documentation, https://github.com/THDayBetter/daybetter-python#readme
|
|
10
|
+
Project-URL: Repository, https://github.com/THDayBetter/daybetter-python.git
|
|
11
|
+
Project-URL: Bug Tracker, https://github.com/THDayBetter/daybetter-python/issues
|
|
12
|
+
Keywords: daybetter,iot,home automation,mqtt
|
|
13
|
+
Classifier: Development Status :: 4 - Beta
|
|
14
|
+
Classifier: Intended Audience :: Developers
|
|
15
|
+
Classifier: Operating System :: OS Independent
|
|
16
|
+
Classifier: Programming Language :: Python :: 3
|
|
17
|
+
Classifier: Programming Language :: Python :: 3.8
|
|
18
|
+
Classifier: Programming Language :: Python :: 3.9
|
|
19
|
+
Classifier: Programming Language :: Python :: 3.10
|
|
20
|
+
Classifier: Programming Language :: Python :: 3.11
|
|
21
|
+
Classifier: Programming Language :: Python :: 3.12
|
|
22
|
+
Classifier: Typing :: Typed
|
|
23
|
+
Requires-Python: >=3.8
|
|
24
|
+
Description-Content-Type: text/markdown
|
|
25
|
+
License-File: LICENSE
|
|
26
|
+
Requires-Dist: aiohttp>=3.8.0
|
|
27
|
+
Provides-Extra: dev
|
|
28
|
+
Requires-Dist: pytest>=6.0; extra == "dev"
|
|
29
|
+
Requires-Dist: pytest-asyncio; extra == "dev"
|
|
30
|
+
Requires-Dist: black; extra == "dev"
|
|
31
|
+
Requires-Dist: isort; extra == "dev"
|
|
32
|
+
Requires-Dist: flake8; extra == "dev"
|
|
33
|
+
Requires-Dist: mypy; extra == "dev"
|
|
34
|
+
Dynamic: license-file
|
|
35
|
+
|
|
36
|
+
# DayBetter Services Python Client
|
|
37
|
+
|
|
38
|
+
[](https://pypi.org/project/daybetter-services-python/)
|
|
39
|
+
[](https://pypi.org/project/daybetter-services-python/)
|
|
40
|
+
[](LICENSE)
|
|
41
|
+
|
|
42
|
+
Asynchronous client library used by the [Home Assistant DayBetter Services integration](https://github.com/home-assistant/core/pull/154677).
|
|
43
|
+
It handles authentication, device discovery, status polling, PID metadata and device control against the DayBetter cloud API.
|
|
44
|
+
|
|
45
|
+
## Features
|
|
46
|
+
|
|
47
|
+
- Fully async `DayBetterClient` with context-manager support
|
|
48
|
+
- Automatic environment selection (test/prod) based on `hass_code`
|
|
49
|
+
- Convenience helpers for common API endpoints (devices, statuses, MQTT config)
|
|
50
|
+
- Sensor-focused workflow `fetch_sensor_data()` that merges devices, statuses and PID filters
|
|
51
|
+
- Type hints + `py.typed` for first-class editor / mypy support
|
|
52
|
+
- Published on PyPI for Home Assistant and other integrations
|
|
53
|
+
|
|
54
|
+
## Installation
|
|
55
|
+
|
|
56
|
+
```bash
|
|
57
|
+
pip install daybetter-services-python
|
|
58
|
+
```
|
|
59
|
+
|
|
60
|
+
## Quick Start
|
|
61
|
+
|
|
62
|
+
```python
|
|
63
|
+
import asyncio
|
|
64
|
+
from daybetter_python import DayBetterClient
|
|
65
|
+
|
|
66
|
+
async def main() -> None:
|
|
67
|
+
async with DayBetterClient(token="YOUR_TOKEN", hass_code="db-xxxx") as client:
|
|
68
|
+
# Fetch merged sensor payloads (used by Home Assistant)
|
|
69
|
+
sensors = await client.fetch_sensor_data()
|
|
70
|
+
for item in sensors:
|
|
71
|
+
print(item["deviceName"], item.get("temp"), item.get("humi"))
|
|
72
|
+
|
|
73
|
+
# Control a device (brightness example)
|
|
74
|
+
await client.control_device(
|
|
75
|
+
device_name="device_001",
|
|
76
|
+
action=True,
|
|
77
|
+
brightness=180,
|
|
78
|
+
)
|
|
79
|
+
|
|
80
|
+
if __name__ == "__main__":
|
|
81
|
+
asyncio.run(main())
|
|
82
|
+
```
|
|
83
|
+
|
|
84
|
+
## Home Assistant Integration
|
|
85
|
+
|
|
86
|
+
The official integration PR can be followed here: [home-assistant/core#154677](https://github.com/home-assistant/core/pull/154677).
|
|
87
|
+
The integration imports this library and simply calls `client.fetch_sensor_data()` inside a data coordinator, so all business logic lives in this package.
|
|
88
|
+
|
|
89
|
+
See `docs/homeassistant.md` for:
|
|
90
|
+
- Installation instructions (pip, custom component)
|
|
91
|
+
- How to obtain the DayBetter token and `hass_code`
|
|
92
|
+
- Sample `configuration.yaml` snippets
|
|
93
|
+
- Known limitations and future roadmap
|
|
94
|
+
|
|
95
|
+
## Development
|
|
96
|
+
|
|
97
|
+
```bash
|
|
98
|
+
git clone https://github.com/THDayBetter/daybetter-services-python.git
|
|
99
|
+
cd daybetter-services-python
|
|
100
|
+
python3 -m venv .venv
|
|
101
|
+
source .venv/bin/activate
|
|
102
|
+
pip install -e .[dev]
|
|
103
|
+
|
|
104
|
+
# Quality checks
|
|
105
|
+
flake8 daybetter_python
|
|
106
|
+
mypy daybetter_python
|
|
107
|
+
pytest
|
|
108
|
+
```
|
|
109
|
+
|
|
110
|
+
Pull requests are welcome! Please open an issue if you encounter problems with the DayBetter cloud API.
|
|
111
|
+
|
|
112
|
+
## Release Process
|
|
113
|
+
|
|
114
|
+
1. Update `pyproject.toml` and `daybetter_python/__init__.py` with the new version.
|
|
115
|
+
2. Document changes in `CHANGELOG.md`.
|
|
116
|
+
3. Build and upload:
|
|
117
|
+
```bash
|
|
118
|
+
rm -rf dist build *.egg-info
|
|
119
|
+
python -m build
|
|
120
|
+
twine check dist/*
|
|
121
|
+
twine upload dist/*
|
|
122
|
+
```
|
|
123
|
+
4. Create a Git tag (e.g., `git tag v1.0.7 && git push origin v1.0.7`).
|
|
124
|
+
|
|
125
|
+
## License
|
|
126
|
+
|
|
127
|
+
This project is licensed under the [MIT License](LICENSE).
|
|
@@ -0,0 +1,92 @@
|
|
|
1
|
+
# DayBetter Services Python Client
|
|
2
|
+
|
|
3
|
+
[](https://pypi.org/project/daybetter-services-python/)
|
|
4
|
+
[](https://pypi.org/project/daybetter-services-python/)
|
|
5
|
+
[](LICENSE)
|
|
6
|
+
|
|
7
|
+
Asynchronous client library used by the [Home Assistant DayBetter Services integration](https://github.com/home-assistant/core/pull/154677).
|
|
8
|
+
It handles authentication, device discovery, status polling, PID metadata and device control against the DayBetter cloud API.
|
|
9
|
+
|
|
10
|
+
## Features
|
|
11
|
+
|
|
12
|
+
- Fully async `DayBetterClient` with context-manager support
|
|
13
|
+
- Automatic environment selection (test/prod) based on `hass_code`
|
|
14
|
+
- Convenience helpers for common API endpoints (devices, statuses, MQTT config)
|
|
15
|
+
- Sensor-focused workflow `fetch_sensor_data()` that merges devices, statuses and PID filters
|
|
16
|
+
- Type hints + `py.typed` for first-class editor / mypy support
|
|
17
|
+
- Published on PyPI for Home Assistant and other integrations
|
|
18
|
+
|
|
19
|
+
## Installation
|
|
20
|
+
|
|
21
|
+
```bash
|
|
22
|
+
pip install daybetter-services-python
|
|
23
|
+
```
|
|
24
|
+
|
|
25
|
+
## Quick Start
|
|
26
|
+
|
|
27
|
+
```python
|
|
28
|
+
import asyncio
|
|
29
|
+
from daybetter_python import DayBetterClient
|
|
30
|
+
|
|
31
|
+
async def main() -> None:
|
|
32
|
+
async with DayBetterClient(token="YOUR_TOKEN", hass_code="db-xxxx") as client:
|
|
33
|
+
# Fetch merged sensor payloads (used by Home Assistant)
|
|
34
|
+
sensors = await client.fetch_sensor_data()
|
|
35
|
+
for item in sensors:
|
|
36
|
+
print(item["deviceName"], item.get("temp"), item.get("humi"))
|
|
37
|
+
|
|
38
|
+
# Control a device (brightness example)
|
|
39
|
+
await client.control_device(
|
|
40
|
+
device_name="device_001",
|
|
41
|
+
action=True,
|
|
42
|
+
brightness=180,
|
|
43
|
+
)
|
|
44
|
+
|
|
45
|
+
if __name__ == "__main__":
|
|
46
|
+
asyncio.run(main())
|
|
47
|
+
```
|
|
48
|
+
|
|
49
|
+
## Home Assistant Integration
|
|
50
|
+
|
|
51
|
+
The official integration PR can be followed here: [home-assistant/core#154677](https://github.com/home-assistant/core/pull/154677).
|
|
52
|
+
The integration imports this library and simply calls `client.fetch_sensor_data()` inside a data coordinator, so all business logic lives in this package.
|
|
53
|
+
|
|
54
|
+
See `docs/homeassistant.md` for:
|
|
55
|
+
- Installation instructions (pip, custom component)
|
|
56
|
+
- How to obtain the DayBetter token and `hass_code`
|
|
57
|
+
- Sample `configuration.yaml` snippets
|
|
58
|
+
- Known limitations and future roadmap
|
|
59
|
+
|
|
60
|
+
## Development
|
|
61
|
+
|
|
62
|
+
```bash
|
|
63
|
+
git clone https://github.com/THDayBetter/daybetter-services-python.git
|
|
64
|
+
cd daybetter-services-python
|
|
65
|
+
python3 -m venv .venv
|
|
66
|
+
source .venv/bin/activate
|
|
67
|
+
pip install -e .[dev]
|
|
68
|
+
|
|
69
|
+
# Quality checks
|
|
70
|
+
flake8 daybetter_python
|
|
71
|
+
mypy daybetter_python
|
|
72
|
+
pytest
|
|
73
|
+
```
|
|
74
|
+
|
|
75
|
+
Pull requests are welcome! Please open an issue if you encounter problems with the DayBetter cloud API.
|
|
76
|
+
|
|
77
|
+
## Release Process
|
|
78
|
+
|
|
79
|
+
1. Update `pyproject.toml` and `daybetter_python/__init__.py` with the new version.
|
|
80
|
+
2. Document changes in `CHANGELOG.md`.
|
|
81
|
+
3. Build and upload:
|
|
82
|
+
```bash
|
|
83
|
+
rm -rf dist build *.egg-info
|
|
84
|
+
python -m build
|
|
85
|
+
twine check dist/*
|
|
86
|
+
twine upload dist/*
|
|
87
|
+
```
|
|
88
|
+
4. Create a Git tag (e.g., `git tag v1.0.7 && git push origin v1.0.7`).
|
|
89
|
+
|
|
90
|
+
## License
|
|
91
|
+
|
|
92
|
+
This project is licensed under the [MIT License](LICENSE).
|
{daybetter_services_python-1.0.6 → daybetter_services_python-1.0.8}/daybetter_python/client.py
RENAMED
|
@@ -1,83 +1,88 @@
|
|
|
1
1
|
"""DayBetter API client."""
|
|
2
2
|
|
|
3
|
-
import aiohttp
|
|
4
3
|
import logging
|
|
5
|
-
from typing import Any, Dict, List, Optional, Tuple
|
|
4
|
+
from typing import Any, Dict, List, Optional, Tuple, Type
|
|
5
|
+
|
|
6
|
+
import aiohttp
|
|
6
7
|
|
|
7
|
-
from .exceptions import
|
|
8
|
+
from .exceptions import APIError, AuthenticationError, DayBetterError
|
|
8
9
|
|
|
9
10
|
_LOGGER = logging.getLogger(__name__)
|
|
10
11
|
|
|
11
12
|
|
|
12
13
|
class DayBetterClient:
|
|
13
14
|
"""DayBetter API client."""
|
|
14
|
-
|
|
15
|
-
# 测试环境URL
|
|
15
|
+
|
|
16
16
|
TEST_BASE_URL = "https://cloud.v2.dbiot.link/daybetter/hass/api/v1.0/"
|
|
17
|
-
# 正式环境URL
|
|
18
17
|
PROD_BASE_URL = "https://a.dbiot.org/daybetter/hass/api/v1.0/"
|
|
19
|
-
|
|
18
|
+
|
|
20
19
|
def __init__(
|
|
21
|
-
self,
|
|
22
|
-
token: str,
|
|
20
|
+
self,
|
|
21
|
+
token: str,
|
|
23
22
|
base_url: Optional[str] = None,
|
|
24
|
-
hass_code: Optional[str] = None
|
|
23
|
+
hass_code: Optional[str] = None,
|
|
24
|
+
session: Optional[aiohttp.ClientSession] = None,
|
|
25
25
|
):
|
|
26
26
|
"""Initialize the client.
|
|
27
|
-
|
|
27
|
+
|
|
28
28
|
Args:
|
|
29
29
|
token: Authentication token
|
|
30
|
-
base_url: Base URL for the API (optional,
|
|
31
|
-
hass_code: Home Assistant integration code (optional, if provided and starts
|
|
32
|
-
|
|
30
|
+
base_url: Base URL for the API (optional, determined by hass_code if not provided)
|
|
31
|
+
hass_code: Home Assistant integration code (optional, if provided and starts
|
|
32
|
+
with "db-", will use production environment)
|
|
33
|
+
session: Optional aiohttp ClientSession. If provided, the client will use it
|
|
34
|
+
and will not close it when close() is called (caller owns the session).
|
|
33
35
|
"""
|
|
34
36
|
self.token = token
|
|
35
|
-
|
|
36
|
-
# 根据 hass_code 或 base_url 确定使用的环境
|
|
37
|
+
|
|
37
38
|
if base_url is not None:
|
|
38
|
-
# 如果明确指定了 base_url,使用指定的 URL
|
|
39
39
|
self.base_url = base_url
|
|
40
40
|
elif hass_code is not None and hass_code.startswith("db-"):
|
|
41
|
-
# 如果 hass_code 以 "db-" 开头,使用正式环境
|
|
42
41
|
self.base_url = self.PROD_BASE_URL
|
|
43
42
|
_LOGGER.debug("Using production environment based on hass_code")
|
|
44
43
|
else:
|
|
45
|
-
# 默认使用测试环境
|
|
46
44
|
self.base_url = self.TEST_BASE_URL
|
|
47
45
|
_LOGGER.debug("Using test environment")
|
|
48
|
-
|
|
49
|
-
self._session: Optional[aiohttp.ClientSession] =
|
|
46
|
+
|
|
47
|
+
self._session: Optional[aiohttp.ClientSession] = session
|
|
48
|
+
self._own_session: bool = session is None
|
|
50
49
|
self._auth_valid = True
|
|
51
50
|
self._devices: List[Dict[str, Any]] = []
|
|
52
51
|
self._pids: Dict[str, Any] = {}
|
|
53
|
-
|
|
52
|
+
|
|
54
53
|
async def __aenter__(self):
|
|
55
54
|
"""Async context manager entry."""
|
|
56
|
-
self._session
|
|
55
|
+
if self._session is None:
|
|
56
|
+
self._session = aiohttp.ClientSession()
|
|
57
57
|
return self
|
|
58
|
-
|
|
59
|
-
async def __aexit__(
|
|
58
|
+
|
|
59
|
+
async def __aexit__(
|
|
60
|
+
self,
|
|
61
|
+
exc_type: Optional[Type[BaseException]],
|
|
62
|
+
exc_val: Optional[BaseException],
|
|
63
|
+
exc_tb: Optional[Any],
|
|
64
|
+
) -> None:
|
|
60
65
|
"""Async context manager exit."""
|
|
61
|
-
if self._session:
|
|
66
|
+
if self._own_session and self._session is not None:
|
|
62
67
|
await self._session.close()
|
|
63
68
|
self._session = None
|
|
64
|
-
|
|
69
|
+
|
|
65
70
|
def _get_session(self) -> aiohttp.ClientSession:
|
|
66
71
|
"""Get or create aiohttp session."""
|
|
67
|
-
if
|
|
72
|
+
if self._session is None:
|
|
68
73
|
self._session = aiohttp.ClientSession()
|
|
69
74
|
return self._session
|
|
70
|
-
|
|
75
|
+
|
|
71
76
|
def _get_headers(self) -> Dict[str, str]:
|
|
72
77
|
"""Get request headers."""
|
|
73
78
|
return {"Authorization": f"Bearer {self.token}"}
|
|
74
|
-
|
|
79
|
+
|
|
75
80
|
async def fetch_devices(self) -> List[Dict[str, Any]]:
|
|
76
81
|
"""Fetch devices from API.
|
|
77
|
-
|
|
82
|
+
|
|
78
83
|
Returns:
|
|
79
84
|
List of device dictionaries
|
|
80
|
-
|
|
85
|
+
|
|
81
86
|
Raises:
|
|
82
87
|
AuthenticationError: If authentication fails
|
|
83
88
|
APIError: If API request fails
|
|
@@ -86,7 +91,7 @@ class DayBetterClient:
|
|
|
86
91
|
session = self._get_session()
|
|
87
92
|
url = f"{self.base_url}hass/devices"
|
|
88
93
|
headers = self._get_headers()
|
|
89
|
-
|
|
94
|
+
|
|
90
95
|
async with session.post(url, headers=headers) as resp:
|
|
91
96
|
if resp.status == 200:
|
|
92
97
|
data = await resp.json()
|
|
@@ -108,13 +113,13 @@ class DayBetterClient:
|
|
|
108
113
|
except Exception as e:
|
|
109
114
|
_LOGGER.exception("Exception while fetching devices: %s", e)
|
|
110
115
|
raise DayBetterError(f"Unexpected error: {e}")
|
|
111
|
-
|
|
116
|
+
|
|
112
117
|
async def fetch_pids(self) -> Dict[str, Any]:
|
|
113
118
|
"""Fetch device type PIDs.
|
|
114
|
-
|
|
119
|
+
|
|
115
120
|
Returns:
|
|
116
121
|
Dictionary of device type PIDs
|
|
117
|
-
|
|
122
|
+
|
|
118
123
|
Raises:
|
|
119
124
|
AuthenticationError: If authentication fails
|
|
120
125
|
APIError: If API request fails
|
|
@@ -123,7 +128,7 @@ class DayBetterClient:
|
|
|
123
128
|
session = self._get_session()
|
|
124
129
|
url = f"{self.base_url}hass/pids"
|
|
125
130
|
headers = self._get_headers()
|
|
126
|
-
|
|
131
|
+
|
|
127
132
|
async with session.post(url, headers=headers) as resp:
|
|
128
133
|
if resp.status == 200:
|
|
129
134
|
data = await resp.json()
|
|
@@ -143,7 +148,7 @@ class DayBetterClient:
|
|
|
143
148
|
except Exception as e:
|
|
144
149
|
_LOGGER.exception("Exception while fetching PIDs: %s", e)
|
|
145
150
|
raise DayBetterError(f"Unexpected error: {e}")
|
|
146
|
-
|
|
151
|
+
|
|
147
152
|
async def control_device(
|
|
148
153
|
self,
|
|
149
154
|
device_name: str,
|
|
@@ -153,17 +158,17 @@ class DayBetterClient:
|
|
|
153
158
|
color_temp: Optional[int] = None,
|
|
154
159
|
) -> Dict[str, Any]:
|
|
155
160
|
"""Control a device.
|
|
156
|
-
|
|
161
|
+
|
|
157
162
|
Args:
|
|
158
163
|
device_name: Name of the device to control
|
|
159
164
|
action: Switch action (True/False)
|
|
160
165
|
brightness: Brightness value (0-255)
|
|
161
166
|
hs_color: Hue and saturation tuple (hue, saturation)
|
|
162
167
|
color_temp: Color temperature in mireds
|
|
163
|
-
|
|
168
|
+
|
|
164
169
|
Returns:
|
|
165
170
|
Control result dictionary
|
|
166
|
-
|
|
171
|
+
|
|
167
172
|
Raises:
|
|
168
173
|
AuthenticationError: If authentication fails
|
|
169
174
|
APIError: If API request fails
|
|
@@ -171,10 +176,8 @@ class DayBetterClient:
|
|
|
171
176
|
session = self._get_session()
|
|
172
177
|
url = f"{self.base_url}hass/control"
|
|
173
178
|
headers = self._get_headers()
|
|
174
|
-
|
|
175
|
-
# Priority: color temperature > color > brightness > switch
|
|
179
|
+
|
|
176
180
|
if color_temp is not None:
|
|
177
|
-
# Convert mireds to Kelvin
|
|
178
181
|
kelvin = int(1000000 / color_temp)
|
|
179
182
|
payload = {
|
|
180
183
|
"deviceName": device_name,
|
|
@@ -193,18 +196,17 @@ class DayBetterClient:
|
|
|
193
196
|
}
|
|
194
197
|
elif brightness is not None:
|
|
195
198
|
payload = {
|
|
196
|
-
"deviceName": device_name,
|
|
197
|
-
"type": 2,
|
|
199
|
+
"deviceName": device_name,
|
|
200
|
+
"type": 2,
|
|
198
201
|
"brightness": brightness
|
|
199
202
|
}
|
|
200
203
|
else:
|
|
201
|
-
# Type 1 control switch is used by default
|
|
202
204
|
payload = {
|
|
203
|
-
"deviceName": device_name,
|
|
204
|
-
"type": 1,
|
|
205
|
+
"deviceName": device_name,
|
|
206
|
+
"type": 1,
|
|
205
207
|
"on": action
|
|
206
208
|
}
|
|
207
|
-
|
|
209
|
+
|
|
208
210
|
try:
|
|
209
211
|
async with session.post(url, headers=headers, json=payload) as resp:
|
|
210
212
|
if resp.status == 200:
|
|
@@ -217,7 +219,7 @@ class DayBetterClient:
|
|
|
217
219
|
else:
|
|
218
220
|
error_text = await resp.text()
|
|
219
221
|
_LOGGER.error(
|
|
220
|
-
"Failed to control device %s: HTTP %d - %s",
|
|
222
|
+
"Failed to control device %s: HTTP %d - %s",
|
|
221
223
|
device_name, resp.status, error_text
|
|
222
224
|
)
|
|
223
225
|
raise APIError(f"API error {resp.status}: {error_text}")
|
|
@@ -231,13 +233,13 @@ class DayBetterClient:
|
|
|
231
233
|
"Exception while controlling device %s: %s", device_name, e
|
|
232
234
|
)
|
|
233
235
|
raise DayBetterError(f"Unexpected error: {e}")
|
|
234
|
-
|
|
236
|
+
|
|
235
237
|
async def fetch_mqtt_config(self) -> Dict[str, Any]:
|
|
236
238
|
"""Fetch MQTT connection configuration.
|
|
237
|
-
|
|
239
|
+
|
|
238
240
|
Returns:
|
|
239
241
|
MQTT configuration dictionary
|
|
240
|
-
|
|
242
|
+
|
|
241
243
|
Raises:
|
|
242
244
|
AuthenticationError: If authentication fails
|
|
243
245
|
APIError: If API request fails
|
|
@@ -246,11 +248,11 @@ class DayBetterClient:
|
|
|
246
248
|
url = f"{self.base_url}hass/cert"
|
|
247
249
|
headers = self._get_headers()
|
|
248
250
|
_LOGGER.debug("Requesting MQTT configuration URL: %s", url)
|
|
249
|
-
|
|
251
|
+
|
|
250
252
|
try:
|
|
251
253
|
async with session.post(url, headers=headers) as resp:
|
|
252
254
|
_LOGGER.debug("MQTT configuration API response status: %d", resp.status)
|
|
253
|
-
|
|
255
|
+
|
|
254
256
|
if resp.status == 200:
|
|
255
257
|
data = await resp.json()
|
|
256
258
|
_LOGGER.debug("MQTT configuration API raw response: %s", data)
|
|
@@ -270,10 +272,10 @@ class DayBetterClient:
|
|
|
270
272
|
except Exception as e:
|
|
271
273
|
_LOGGER.exception("Exception while fetching MQTT config: %s", e)
|
|
272
274
|
raise DayBetterError(f"Unexpected error: {e}")
|
|
273
|
-
|
|
275
|
+
|
|
274
276
|
async def fetch_device_statuses(self) -> List[Dict[str, Any]]:
|
|
275
277
|
"""Fetch statuses for all devices.
|
|
276
|
-
|
|
278
|
+
|
|
277
279
|
Returns:
|
|
278
280
|
List of device status dictionaries. Example item:
|
|
279
281
|
{
|
|
@@ -284,7 +286,7 @@ class DayBetterClient:
|
|
|
284
286
|
"humi": int,
|
|
285
287
|
"bettery": int
|
|
286
288
|
}
|
|
287
|
-
|
|
289
|
+
|
|
288
290
|
Raises:
|
|
289
291
|
AuthenticationError: If authentication fails
|
|
290
292
|
APIError: If API request fails
|
|
@@ -293,12 +295,11 @@ class DayBetterClient:
|
|
|
293
295
|
session = self._get_session()
|
|
294
296
|
url = f"{self.base_url}hass/status"
|
|
295
297
|
headers = self._get_headers()
|
|
296
|
-
|
|
298
|
+
|
|
297
299
|
async with session.post(url, headers=headers) as resp:
|
|
298
300
|
if resp.status == 200:
|
|
299
301
|
data = await resp.json()
|
|
300
302
|
self._auth_valid = True
|
|
301
|
-
# API expected to return { "data": [...] }
|
|
302
303
|
return data.get("data", [])
|
|
303
304
|
elif resp.status == 401:
|
|
304
305
|
_LOGGER.error("Authentication failed - token may be expired")
|
|
@@ -314,21 +315,19 @@ class DayBetterClient:
|
|
|
314
315
|
except Exception as e:
|
|
315
316
|
_LOGGER.exception("Exception while fetching device statuses: %s", e)
|
|
316
317
|
raise DayBetterError(f"Unexpected error: {e}")
|
|
317
|
-
|
|
318
|
+
|
|
318
319
|
async def integrate(self, hass_code: str) -> Dict[str, Any]:
|
|
319
320
|
"""Integrate with Home Assistant using hassCode.
|
|
320
|
-
|
|
321
|
+
|
|
321
322
|
Args:
|
|
322
323
|
hass_code: Home Assistant integration code from APP
|
|
323
|
-
|
|
324
|
+
|
|
324
325
|
Returns:
|
|
325
326
|
Integration result dictionary
|
|
326
|
-
|
|
327
|
+
|
|
327
328
|
Raises:
|
|
328
329
|
APIError: If API request fails
|
|
329
330
|
"""
|
|
330
|
-
# 根据 hass_code 动态更新 base_url(如果之前没有明确指定)
|
|
331
|
-
# 如果 hass_code 以 "db-" 开头,切换到正式环境
|
|
332
331
|
if hass_code.startswith("db-") and self.base_url != self.PROD_BASE_URL:
|
|
333
332
|
old_url = self.base_url
|
|
334
333
|
self.base_url = self.PROD_BASE_URL
|
|
@@ -337,19 +336,18 @@ class DayBetterClient:
|
|
|
337
336
|
"URL changed from %s to %s", old_url, self.base_url
|
|
338
337
|
)
|
|
339
338
|
elif not hass_code.startswith("db-") and self.base_url != self.TEST_BASE_URL:
|
|
340
|
-
# 如果 hass_code 不以 "db-" 开头,且当前不是测试环境,切换到测试环境
|
|
341
339
|
old_url = self.base_url
|
|
342
340
|
self.base_url = self.TEST_BASE_URL
|
|
343
341
|
_LOGGER.info(
|
|
344
342
|
"Switching to test environment based on hass_code. "
|
|
345
343
|
"URL changed from %s to %s", old_url, self.base_url
|
|
346
344
|
)
|
|
347
|
-
|
|
345
|
+
|
|
348
346
|
try:
|
|
349
347
|
session = self._get_session()
|
|
350
348
|
url = f"{self.base_url}hass/integrate"
|
|
351
349
|
payload = {"hassCode": hass_code}
|
|
352
|
-
|
|
350
|
+
|
|
353
351
|
async with session.post(url, json=payload) as resp:
|
|
354
352
|
if resp.status == 200:
|
|
355
353
|
data = await resp.json()
|
|
@@ -365,23 +363,23 @@ class DayBetterClient:
|
|
|
365
363
|
except Exception as e:
|
|
366
364
|
_LOGGER.exception("Exception while integrating: %s", e)
|
|
367
365
|
raise DayBetterError(f"Unexpected error: {e}")
|
|
368
|
-
|
|
366
|
+
|
|
369
367
|
@property
|
|
370
368
|
def is_authenticated(self) -> bool:
|
|
371
369
|
"""Check if the API client is authenticated."""
|
|
372
370
|
return self._auth_valid
|
|
373
|
-
|
|
371
|
+
|
|
374
372
|
def filter_sensor_devices(
|
|
375
373
|
self,
|
|
376
374
|
devices: List[Dict[str, Any]],
|
|
377
375
|
pids: Dict[str, Any],
|
|
378
376
|
) -> List[Dict[str, Any]]:
|
|
379
377
|
"""Filter devices to only include sensors based on PID.
|
|
380
|
-
|
|
378
|
+
|
|
381
379
|
Args:
|
|
382
380
|
devices: List of all devices
|
|
383
381
|
pids: Dictionary containing device type PIDs
|
|
384
|
-
|
|
382
|
+
|
|
385
383
|
Returns:
|
|
386
384
|
List of sensor devices only
|
|
387
385
|
"""
|
|
@@ -396,18 +394,18 @@ class DayBetterClient:
|
|
|
396
394
|
for device in devices
|
|
397
395
|
if device.get("deviceMoldPid", "") in sensor_pids
|
|
398
396
|
]
|
|
399
|
-
|
|
397
|
+
|
|
400
398
|
def merge_device_status(
|
|
401
399
|
self,
|
|
402
400
|
devices: List[Dict[str, Any]],
|
|
403
401
|
statuses: List[Dict[str, Any]],
|
|
404
402
|
) -> List[Dict[str, Any]]:
|
|
405
403
|
"""Merge device info with status info.
|
|
406
|
-
|
|
404
|
+
|
|
407
405
|
Args:
|
|
408
406
|
devices: List of device info dictionaries
|
|
409
407
|
statuses: List of device status dictionaries
|
|
410
|
-
|
|
408
|
+
|
|
411
409
|
Returns:
|
|
412
410
|
List of merged device dictionaries
|
|
413
411
|
"""
|
|
@@ -424,38 +422,34 @@ class DayBetterClient:
|
|
|
424
422
|
merged.append(merged_device)
|
|
425
423
|
|
|
426
424
|
return merged
|
|
427
|
-
|
|
425
|
+
|
|
428
426
|
async def fetch_sensor_data(self) -> List[Dict[str, Any]]:
|
|
429
427
|
"""Fetch and process sensor data in one call.
|
|
430
|
-
|
|
428
|
+
|
|
431
429
|
This method fetches device statuses, devices list, and PIDs,
|
|
432
430
|
filters for sensor devices, and merges the data.
|
|
433
|
-
|
|
431
|
+
|
|
434
432
|
Returns:
|
|
435
433
|
List of sensor devices with merged status data
|
|
436
|
-
|
|
434
|
+
|
|
437
435
|
Raises:
|
|
438
436
|
AuthenticationError: If authentication fails
|
|
439
437
|
APIError: If API request fails
|
|
440
438
|
"""
|
|
441
|
-
# Fetch current statuses
|
|
442
439
|
statuses = await self.fetch_device_statuses()
|
|
443
|
-
|
|
444
|
-
# Fetch devices and PIDs if not cached
|
|
440
|
+
|
|
445
441
|
if not self._devices or not self._pids:
|
|
446
442
|
self._devices = await self.fetch_devices()
|
|
447
443
|
self._pids = await self.fetch_pids()
|
|
448
|
-
|
|
449
|
-
# Filter to sensor devices only
|
|
444
|
+
|
|
450
445
|
sensor_devices = self.filter_sensor_devices(self._devices, self._pids)
|
|
451
|
-
|
|
452
|
-
# Merge with current status
|
|
446
|
+
|
|
453
447
|
merged = self.merge_device_status(sensor_devices, statuses)
|
|
454
448
|
_LOGGER.debug("Fetched %d sensor devices", len(merged))
|
|
455
449
|
return merged
|
|
456
|
-
|
|
457
|
-
async def close(self):
|
|
458
|
-
"""Close the client session."""
|
|
459
|
-
if self._session:
|
|
450
|
+
|
|
451
|
+
async def close(self) -> None:
|
|
452
|
+
"""Close the client session if this client created it."""
|
|
453
|
+
if self._own_session and self._session is not None:
|
|
460
454
|
await self._session.close()
|
|
461
455
|
self._session = None
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
"""DayBetter client exceptions."""
|
|
2
|
+
|
|
3
|
+
|
|
4
|
+
class DayBetterError(Exception):
|
|
5
|
+
"""Base exception for DayBetter client."""
|
|
6
|
+
|
|
7
|
+
def __init__(self, message: str) -> None:
|
|
8
|
+
"""Initialize the exception.
|
|
9
|
+
|
|
10
|
+
Args:
|
|
11
|
+
message: Error message
|
|
12
|
+
"""
|
|
13
|
+
super().__init__(message)
|
|
14
|
+
self.message = message
|
|
15
|
+
|
|
16
|
+
|
|
17
|
+
class AuthenticationError(DayBetterError):
|
|
18
|
+
"""Authentication failed."""
|
|
19
|
+
|
|
20
|
+
def __init__(self, message: str = "Authentication failed") -> None:
|
|
21
|
+
"""Initialize the authentication error.
|
|
22
|
+
|
|
23
|
+
Args:
|
|
24
|
+
message: Error message
|
|
25
|
+
"""
|
|
26
|
+
super().__init__(message)
|
|
27
|
+
|
|
28
|
+
|
|
29
|
+
class APIError(DayBetterError):
|
|
30
|
+
"""API request failed."""
|
|
31
|
+
|
|
32
|
+
def __init__(self, message: str) -> None:
|
|
33
|
+
"""Initialize the API error.
|
|
34
|
+
|
|
35
|
+
Args:
|
|
36
|
+
message: Error message
|
|
37
|
+
"""
|
|
38
|
+
super().__init__(message)
|
|
File without changes
|
|
@@ -0,0 +1,127 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: daybetter-services-python
|
|
3
|
+
Version: 1.0.8
|
|
4
|
+
Summary: Python client for DayBetter devices and services
|
|
5
|
+
Author-email: THDayBetter <chenp2368@163.com>
|
|
6
|
+
Maintainer-email: THDayBetter <chenp2368@163.com>
|
|
7
|
+
License-Expression: MIT
|
|
8
|
+
Project-URL: Homepage, https://github.com/THDayBetter/daybetter-python
|
|
9
|
+
Project-URL: Documentation, https://github.com/THDayBetter/daybetter-python#readme
|
|
10
|
+
Project-URL: Repository, https://github.com/THDayBetter/daybetter-python.git
|
|
11
|
+
Project-URL: Bug Tracker, https://github.com/THDayBetter/daybetter-python/issues
|
|
12
|
+
Keywords: daybetter,iot,home automation,mqtt
|
|
13
|
+
Classifier: Development Status :: 4 - Beta
|
|
14
|
+
Classifier: Intended Audience :: Developers
|
|
15
|
+
Classifier: Operating System :: OS Independent
|
|
16
|
+
Classifier: Programming Language :: Python :: 3
|
|
17
|
+
Classifier: Programming Language :: Python :: 3.8
|
|
18
|
+
Classifier: Programming Language :: Python :: 3.9
|
|
19
|
+
Classifier: Programming Language :: Python :: 3.10
|
|
20
|
+
Classifier: Programming Language :: Python :: 3.11
|
|
21
|
+
Classifier: Programming Language :: Python :: 3.12
|
|
22
|
+
Classifier: Typing :: Typed
|
|
23
|
+
Requires-Python: >=3.8
|
|
24
|
+
Description-Content-Type: text/markdown
|
|
25
|
+
License-File: LICENSE
|
|
26
|
+
Requires-Dist: aiohttp>=3.8.0
|
|
27
|
+
Provides-Extra: dev
|
|
28
|
+
Requires-Dist: pytest>=6.0; extra == "dev"
|
|
29
|
+
Requires-Dist: pytest-asyncio; extra == "dev"
|
|
30
|
+
Requires-Dist: black; extra == "dev"
|
|
31
|
+
Requires-Dist: isort; extra == "dev"
|
|
32
|
+
Requires-Dist: flake8; extra == "dev"
|
|
33
|
+
Requires-Dist: mypy; extra == "dev"
|
|
34
|
+
Dynamic: license-file
|
|
35
|
+
|
|
36
|
+
# DayBetter Services Python Client
|
|
37
|
+
|
|
38
|
+
[](https://pypi.org/project/daybetter-services-python/)
|
|
39
|
+
[](https://pypi.org/project/daybetter-services-python/)
|
|
40
|
+
[](LICENSE)
|
|
41
|
+
|
|
42
|
+
Asynchronous client library used by the [Home Assistant DayBetter Services integration](https://github.com/home-assistant/core/pull/154677).
|
|
43
|
+
It handles authentication, device discovery, status polling, PID metadata and device control against the DayBetter cloud API.
|
|
44
|
+
|
|
45
|
+
## Features
|
|
46
|
+
|
|
47
|
+
- Fully async `DayBetterClient` with context-manager support
|
|
48
|
+
- Automatic environment selection (test/prod) based on `hass_code`
|
|
49
|
+
- Convenience helpers for common API endpoints (devices, statuses, MQTT config)
|
|
50
|
+
- Sensor-focused workflow `fetch_sensor_data()` that merges devices, statuses and PID filters
|
|
51
|
+
- Type hints + `py.typed` for first-class editor / mypy support
|
|
52
|
+
- Published on PyPI for Home Assistant and other integrations
|
|
53
|
+
|
|
54
|
+
## Installation
|
|
55
|
+
|
|
56
|
+
```bash
|
|
57
|
+
pip install daybetter-services-python
|
|
58
|
+
```
|
|
59
|
+
|
|
60
|
+
## Quick Start
|
|
61
|
+
|
|
62
|
+
```python
|
|
63
|
+
import asyncio
|
|
64
|
+
from daybetter_python import DayBetterClient
|
|
65
|
+
|
|
66
|
+
async def main() -> None:
|
|
67
|
+
async with DayBetterClient(token="YOUR_TOKEN", hass_code="db-xxxx") as client:
|
|
68
|
+
# Fetch merged sensor payloads (used by Home Assistant)
|
|
69
|
+
sensors = await client.fetch_sensor_data()
|
|
70
|
+
for item in sensors:
|
|
71
|
+
print(item["deviceName"], item.get("temp"), item.get("humi"))
|
|
72
|
+
|
|
73
|
+
# Control a device (brightness example)
|
|
74
|
+
await client.control_device(
|
|
75
|
+
device_name="device_001",
|
|
76
|
+
action=True,
|
|
77
|
+
brightness=180,
|
|
78
|
+
)
|
|
79
|
+
|
|
80
|
+
if __name__ == "__main__":
|
|
81
|
+
asyncio.run(main())
|
|
82
|
+
```
|
|
83
|
+
|
|
84
|
+
## Home Assistant Integration
|
|
85
|
+
|
|
86
|
+
The official integration PR can be followed here: [home-assistant/core#154677](https://github.com/home-assistant/core/pull/154677).
|
|
87
|
+
The integration imports this library and simply calls `client.fetch_sensor_data()` inside a data coordinator, so all business logic lives in this package.
|
|
88
|
+
|
|
89
|
+
See `docs/homeassistant.md` for:
|
|
90
|
+
- Installation instructions (pip, custom component)
|
|
91
|
+
- How to obtain the DayBetter token and `hass_code`
|
|
92
|
+
- Sample `configuration.yaml` snippets
|
|
93
|
+
- Known limitations and future roadmap
|
|
94
|
+
|
|
95
|
+
## Development
|
|
96
|
+
|
|
97
|
+
```bash
|
|
98
|
+
git clone https://github.com/THDayBetter/daybetter-services-python.git
|
|
99
|
+
cd daybetter-services-python
|
|
100
|
+
python3 -m venv .venv
|
|
101
|
+
source .venv/bin/activate
|
|
102
|
+
pip install -e .[dev]
|
|
103
|
+
|
|
104
|
+
# Quality checks
|
|
105
|
+
flake8 daybetter_python
|
|
106
|
+
mypy daybetter_python
|
|
107
|
+
pytest
|
|
108
|
+
```
|
|
109
|
+
|
|
110
|
+
Pull requests are welcome! Please open an issue if you encounter problems with the DayBetter cloud API.
|
|
111
|
+
|
|
112
|
+
## Release Process
|
|
113
|
+
|
|
114
|
+
1. Update `pyproject.toml` and `daybetter_python/__init__.py` with the new version.
|
|
115
|
+
2. Document changes in `CHANGELOG.md`.
|
|
116
|
+
3. Build and upload:
|
|
117
|
+
```bash
|
|
118
|
+
rm -rf dist build *.egg-info
|
|
119
|
+
python -m build
|
|
120
|
+
twine check dist/*
|
|
121
|
+
twine upload dist/*
|
|
122
|
+
```
|
|
123
|
+
4. Create a Git tag (e.g., `git tag v1.0.7 && git push origin v1.0.7`).
|
|
124
|
+
|
|
125
|
+
## License
|
|
126
|
+
|
|
127
|
+
This project is licensed under the [MIT License](LICENSE).
|
|
@@ -1,12 +1,13 @@
|
|
|
1
1
|
LICENSE
|
|
2
2
|
README.md
|
|
3
3
|
pyproject.toml
|
|
4
|
-
setup.py
|
|
5
4
|
daybetter_python/__init__.py
|
|
6
5
|
daybetter_python/client.py
|
|
7
6
|
daybetter_python/exceptions.py
|
|
7
|
+
daybetter_python/py.typed
|
|
8
8
|
daybetter_services_python.egg-info/PKG-INFO
|
|
9
9
|
daybetter_services_python.egg-info/SOURCES.txt
|
|
10
10
|
daybetter_services_python.egg-info/dependency_links.txt
|
|
11
11
|
daybetter_services_python.egg-info/requires.txt
|
|
12
|
-
daybetter_services_python.egg-info/top_level.txt
|
|
12
|
+
daybetter_services_python.egg-info/top_level.txt
|
|
13
|
+
tests/test_placeholder.py
|
|
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
|
|
|
4
4
|
|
|
5
5
|
[project]
|
|
6
6
|
name = "daybetter-services-python"
|
|
7
|
-
version = "1.0.
|
|
7
|
+
version = "1.0.8"
|
|
8
8
|
description = "Python client for DayBetter devices and services"
|
|
9
9
|
readme = "README.md"
|
|
10
10
|
license = "MIT"
|
|
@@ -25,6 +25,7 @@ classifiers = [
|
|
|
25
25
|
"Programming Language :: Python :: 3.10",
|
|
26
26
|
"Programming Language :: Python :: 3.11",
|
|
27
27
|
"Programming Language :: Python :: 3.12",
|
|
28
|
+
"Typing :: Typed",
|
|
28
29
|
]
|
|
29
30
|
requires-python = ">=3.8"
|
|
30
31
|
dependencies = [
|
|
@@ -55,3 +56,10 @@ target-version = ['py38']
|
|
|
55
56
|
[tool.isort]
|
|
56
57
|
profile = "black"
|
|
57
58
|
line_length = 88
|
|
59
|
+
|
|
60
|
+
[tool.flake8]
|
|
61
|
+
max-line-length = 100
|
|
62
|
+
extend-ignore = ["W503"]
|
|
63
|
+
|
|
64
|
+
[tool.setuptools.package-data]
|
|
65
|
+
daybetter_python = ["py.typed"]
|
|
@@ -1,98 +0,0 @@
|
|
|
1
|
-
Metadata-Version: 2.4
|
|
2
|
-
Name: daybetter-services-python
|
|
3
|
-
Version: 1.0.6
|
|
4
|
-
Summary: Python client for DayBetter devices and services
|
|
5
|
-
Home-page: https://github.com/THDayBetter/daybetter-python
|
|
6
|
-
Author: THDayBetter
|
|
7
|
-
Author-email: THDayBetter <chenp2368@163.com>
|
|
8
|
-
Maintainer-email: THDayBetter <chenp2368@163.com>
|
|
9
|
-
License-Expression: MIT
|
|
10
|
-
Project-URL: Homepage, https://github.com/THDayBetter/daybetter-python
|
|
11
|
-
Project-URL: Documentation, https://github.com/THDayBetter/daybetter-python#readme
|
|
12
|
-
Project-URL: Repository, https://github.com/THDayBetter/daybetter-python.git
|
|
13
|
-
Project-URL: Bug Tracker, https://github.com/THDayBetter/daybetter-python/issues
|
|
14
|
-
Keywords: daybetter,iot,home automation,mqtt
|
|
15
|
-
Classifier: Development Status :: 4 - Beta
|
|
16
|
-
Classifier: Intended Audience :: Developers
|
|
17
|
-
Classifier: Operating System :: OS Independent
|
|
18
|
-
Classifier: Programming Language :: Python :: 3
|
|
19
|
-
Classifier: Programming Language :: Python :: 3.8
|
|
20
|
-
Classifier: Programming Language :: Python :: 3.9
|
|
21
|
-
Classifier: Programming Language :: Python :: 3.10
|
|
22
|
-
Classifier: Programming Language :: Python :: 3.11
|
|
23
|
-
Classifier: Programming Language :: Python :: 3.12
|
|
24
|
-
Requires-Python: >=3.8
|
|
25
|
-
Description-Content-Type: text/markdown
|
|
26
|
-
License-File: LICENSE
|
|
27
|
-
Requires-Dist: aiohttp>=3.8.0
|
|
28
|
-
Provides-Extra: dev
|
|
29
|
-
Requires-Dist: pytest>=6.0; extra == "dev"
|
|
30
|
-
Requires-Dist: pytest-asyncio; extra == "dev"
|
|
31
|
-
Requires-Dist: black; extra == "dev"
|
|
32
|
-
Requires-Dist: isort; extra == "dev"
|
|
33
|
-
Requires-Dist: flake8; extra == "dev"
|
|
34
|
-
Requires-Dist: mypy; extra == "dev"
|
|
35
|
-
Dynamic: author
|
|
36
|
-
Dynamic: home-page
|
|
37
|
-
Dynamic: license-file
|
|
38
|
-
Dynamic: requires-python
|
|
39
|
-
|
|
40
|
-
# DayBetter Python Client
|
|
41
|
-
|
|
42
|
-
A Python client library for interacting with DayBetter devices and services.
|
|
43
|
-
|
|
44
|
-
## Features
|
|
45
|
-
|
|
46
|
-
- Device management and control
|
|
47
|
-
- MQTT configuration retrieval
|
|
48
|
-
- Authentication handling
|
|
49
|
-
- Async/await support
|
|
50
|
-
|
|
51
|
-
## Installation
|
|
52
|
-
|
|
53
|
-
```bash
|
|
54
|
-
pip install daybetter-services-python
|
|
55
|
-
```
|
|
56
|
-
|
|
57
|
-
## Usage
|
|
58
|
-
|
|
59
|
-
```python
|
|
60
|
-
import asyncio
|
|
61
|
-
from daybetter_python import DayBetterClient
|
|
62
|
-
|
|
63
|
-
async def main():
|
|
64
|
-
async with DayBetterClient(token="your_token") as client:
|
|
65
|
-
# Fetch devices
|
|
66
|
-
devices = await client.fetch_devices()
|
|
67
|
-
print(f"Found {len(devices)} devices")
|
|
68
|
-
|
|
69
|
-
# Control a device
|
|
70
|
-
result = await client.control_device(
|
|
71
|
-
device_name="device_001",
|
|
72
|
-
action=True,
|
|
73
|
-
brightness=80
|
|
74
|
-
)
|
|
75
|
-
print(f"Control result: {result}")
|
|
76
|
-
|
|
77
|
-
if __name__ == "__main__":
|
|
78
|
-
asyncio.run(main())
|
|
79
|
-
```
|
|
80
|
-
|
|
81
|
-
## API Reference
|
|
82
|
-
|
|
83
|
-
### DayBetterClient
|
|
84
|
-
|
|
85
|
-
#### Methods
|
|
86
|
-
|
|
87
|
-
- `fetch_devices()`: Get list of devices
|
|
88
|
-
- `fetch_pids()`: Get device type PIDs
|
|
89
|
-
- `control_device(device_name, action, brightness, hs_color, color_temp)`: Control a device
|
|
90
|
-
- `fetch_mqtt_config()`: Get MQTT configuration
|
|
91
|
-
|
|
92
|
-
## License
|
|
93
|
-
|
|
94
|
-
MIT License
|
|
95
|
-
|
|
96
|
-
## Contributing
|
|
97
|
-
|
|
98
|
-
Contributions are welcome! Please feel free to submit a Pull Request.
|
|
@@ -1,59 +0,0 @@
|
|
|
1
|
-
# DayBetter Python Client
|
|
2
|
-
|
|
3
|
-
A Python client library for interacting with DayBetter devices and services.
|
|
4
|
-
|
|
5
|
-
## Features
|
|
6
|
-
|
|
7
|
-
- Device management and control
|
|
8
|
-
- MQTT configuration retrieval
|
|
9
|
-
- Authentication handling
|
|
10
|
-
- Async/await support
|
|
11
|
-
|
|
12
|
-
## Installation
|
|
13
|
-
|
|
14
|
-
```bash
|
|
15
|
-
pip install daybetter-services-python
|
|
16
|
-
```
|
|
17
|
-
|
|
18
|
-
## Usage
|
|
19
|
-
|
|
20
|
-
```python
|
|
21
|
-
import asyncio
|
|
22
|
-
from daybetter_python import DayBetterClient
|
|
23
|
-
|
|
24
|
-
async def main():
|
|
25
|
-
async with DayBetterClient(token="your_token") as client:
|
|
26
|
-
# Fetch devices
|
|
27
|
-
devices = await client.fetch_devices()
|
|
28
|
-
print(f"Found {len(devices)} devices")
|
|
29
|
-
|
|
30
|
-
# Control a device
|
|
31
|
-
result = await client.control_device(
|
|
32
|
-
device_name="device_001",
|
|
33
|
-
action=True,
|
|
34
|
-
brightness=80
|
|
35
|
-
)
|
|
36
|
-
print(f"Control result: {result}")
|
|
37
|
-
|
|
38
|
-
if __name__ == "__main__":
|
|
39
|
-
asyncio.run(main())
|
|
40
|
-
```
|
|
41
|
-
|
|
42
|
-
## API Reference
|
|
43
|
-
|
|
44
|
-
### DayBetterClient
|
|
45
|
-
|
|
46
|
-
#### Methods
|
|
47
|
-
|
|
48
|
-
- `fetch_devices()`: Get list of devices
|
|
49
|
-
- `fetch_pids()`: Get device type PIDs
|
|
50
|
-
- `control_device(device_name, action, brightness, hs_color, color_temp)`: Control a device
|
|
51
|
-
- `fetch_mqtt_config()`: Get MQTT configuration
|
|
52
|
-
|
|
53
|
-
## License
|
|
54
|
-
|
|
55
|
-
MIT License
|
|
56
|
-
|
|
57
|
-
## Contributing
|
|
58
|
-
|
|
59
|
-
Contributions are welcome! Please feel free to submit a Pull Request.
|
|
@@ -1,13 +0,0 @@
|
|
|
1
|
-
"""DayBetter client exceptions."""
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
class DayBetterError(Exception):
|
|
5
|
-
"""Base exception for DayBetter client."""
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
class AuthenticationError(DayBetterError):
|
|
9
|
-
"""Authentication failed."""
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
class APIError(DayBetterError):
|
|
13
|
-
"""API request failed."""
|
|
@@ -1,98 +0,0 @@
|
|
|
1
|
-
Metadata-Version: 2.4
|
|
2
|
-
Name: daybetter-services-python
|
|
3
|
-
Version: 1.0.6
|
|
4
|
-
Summary: Python client for DayBetter devices and services
|
|
5
|
-
Home-page: https://github.com/THDayBetter/daybetter-python
|
|
6
|
-
Author: THDayBetter
|
|
7
|
-
Author-email: THDayBetter <chenp2368@163.com>
|
|
8
|
-
Maintainer-email: THDayBetter <chenp2368@163.com>
|
|
9
|
-
License-Expression: MIT
|
|
10
|
-
Project-URL: Homepage, https://github.com/THDayBetter/daybetter-python
|
|
11
|
-
Project-URL: Documentation, https://github.com/THDayBetter/daybetter-python#readme
|
|
12
|
-
Project-URL: Repository, https://github.com/THDayBetter/daybetter-python.git
|
|
13
|
-
Project-URL: Bug Tracker, https://github.com/THDayBetter/daybetter-python/issues
|
|
14
|
-
Keywords: daybetter,iot,home automation,mqtt
|
|
15
|
-
Classifier: Development Status :: 4 - Beta
|
|
16
|
-
Classifier: Intended Audience :: Developers
|
|
17
|
-
Classifier: Operating System :: OS Independent
|
|
18
|
-
Classifier: Programming Language :: Python :: 3
|
|
19
|
-
Classifier: Programming Language :: Python :: 3.8
|
|
20
|
-
Classifier: Programming Language :: Python :: 3.9
|
|
21
|
-
Classifier: Programming Language :: Python :: 3.10
|
|
22
|
-
Classifier: Programming Language :: Python :: 3.11
|
|
23
|
-
Classifier: Programming Language :: Python :: 3.12
|
|
24
|
-
Requires-Python: >=3.8
|
|
25
|
-
Description-Content-Type: text/markdown
|
|
26
|
-
License-File: LICENSE
|
|
27
|
-
Requires-Dist: aiohttp>=3.8.0
|
|
28
|
-
Provides-Extra: dev
|
|
29
|
-
Requires-Dist: pytest>=6.0; extra == "dev"
|
|
30
|
-
Requires-Dist: pytest-asyncio; extra == "dev"
|
|
31
|
-
Requires-Dist: black; extra == "dev"
|
|
32
|
-
Requires-Dist: isort; extra == "dev"
|
|
33
|
-
Requires-Dist: flake8; extra == "dev"
|
|
34
|
-
Requires-Dist: mypy; extra == "dev"
|
|
35
|
-
Dynamic: author
|
|
36
|
-
Dynamic: home-page
|
|
37
|
-
Dynamic: license-file
|
|
38
|
-
Dynamic: requires-python
|
|
39
|
-
|
|
40
|
-
# DayBetter Python Client
|
|
41
|
-
|
|
42
|
-
A Python client library for interacting with DayBetter devices and services.
|
|
43
|
-
|
|
44
|
-
## Features
|
|
45
|
-
|
|
46
|
-
- Device management and control
|
|
47
|
-
- MQTT configuration retrieval
|
|
48
|
-
- Authentication handling
|
|
49
|
-
- Async/await support
|
|
50
|
-
|
|
51
|
-
## Installation
|
|
52
|
-
|
|
53
|
-
```bash
|
|
54
|
-
pip install daybetter-services-python
|
|
55
|
-
```
|
|
56
|
-
|
|
57
|
-
## Usage
|
|
58
|
-
|
|
59
|
-
```python
|
|
60
|
-
import asyncio
|
|
61
|
-
from daybetter_python import DayBetterClient
|
|
62
|
-
|
|
63
|
-
async def main():
|
|
64
|
-
async with DayBetterClient(token="your_token") as client:
|
|
65
|
-
# Fetch devices
|
|
66
|
-
devices = await client.fetch_devices()
|
|
67
|
-
print(f"Found {len(devices)} devices")
|
|
68
|
-
|
|
69
|
-
# Control a device
|
|
70
|
-
result = await client.control_device(
|
|
71
|
-
device_name="device_001",
|
|
72
|
-
action=True,
|
|
73
|
-
brightness=80
|
|
74
|
-
)
|
|
75
|
-
print(f"Control result: {result}")
|
|
76
|
-
|
|
77
|
-
if __name__ == "__main__":
|
|
78
|
-
asyncio.run(main())
|
|
79
|
-
```
|
|
80
|
-
|
|
81
|
-
## API Reference
|
|
82
|
-
|
|
83
|
-
### DayBetterClient
|
|
84
|
-
|
|
85
|
-
#### Methods
|
|
86
|
-
|
|
87
|
-
- `fetch_devices()`: Get list of devices
|
|
88
|
-
- `fetch_pids()`: Get device type PIDs
|
|
89
|
-
- `control_device(device_name, action, brightness, hs_color, color_temp)`: Control a device
|
|
90
|
-
- `fetch_mqtt_config()`: Get MQTT configuration
|
|
91
|
-
|
|
92
|
-
## License
|
|
93
|
-
|
|
94
|
-
MIT License
|
|
95
|
-
|
|
96
|
-
## Contributing
|
|
97
|
-
|
|
98
|
-
Contributions are welcome! Please feel free to submit a Pull Request.
|
|
@@ -1,34 +0,0 @@
|
|
|
1
|
-
from setuptools import setup, find_packages
|
|
2
|
-
|
|
3
|
-
with open("README.md", "r", encoding="utf-8") as fh:
|
|
4
|
-
long_description = fh.read()
|
|
5
|
-
|
|
6
|
-
setup(
|
|
7
|
-
name="daybetter-services-python",
|
|
8
|
-
version="1.0.6",
|
|
9
|
-
author="THDayBetter",
|
|
10
|
-
author_email="chenp2368@163.com",
|
|
11
|
-
description="Python client for DayBetter devices and services",
|
|
12
|
-
long_description=long_description,
|
|
13
|
-
long_description_content_type="text/markdown",
|
|
14
|
-
url="https://github.com/THDayBetter/daybetter-python",
|
|
15
|
-
packages=find_packages(),
|
|
16
|
-
classifiers=[
|
|
17
|
-
"Development Status :: 4 - Beta",
|
|
18
|
-
"Intended Audience :: Developers",
|
|
19
|
-
"Operating System :: OS Independent",
|
|
20
|
-
"Programming Language :: Python :: 3",
|
|
21
|
-
"Programming Language :: Python :: 3.8",
|
|
22
|
-
"Programming Language :: Python :: 3.9",
|
|
23
|
-
"Programming Language :: Python :: 3.10",
|
|
24
|
-
"Programming Language :: Python :: 3.11",
|
|
25
|
-
"Programming Language :: Python :: 3.12",
|
|
26
|
-
],
|
|
27
|
-
python_requires=">=3.8",
|
|
28
|
-
install_requires=["aiohttp>=3.8.0"],
|
|
29
|
-
keywords="daybetter, iot, home automation, mqtt",
|
|
30
|
-
project_urls={
|
|
31
|
-
"Bug Reports": "https://github.com/THDayBetter/daybetter-python/issues",
|
|
32
|
-
"Source": "https://github.com/THDayBetter/daybetter-python",
|
|
33
|
-
},
|
|
34
|
-
)
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|