daybetter-services-python 1.0.7__tar.gz → 1.0.9__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.9/PKG-INFO +127 -0
- daybetter_services_python-1.0.9/README.md +92 -0
- {daybetter_services_python-1.0.7 → daybetter_services_python-1.0.9}/daybetter_python/__init__.py +1 -1
- {daybetter_services_python-1.0.7 → daybetter_services_python-1.0.9}/daybetter_python/client.py +78 -89
- {daybetter_services_python-1.0.7 → daybetter_services_python-1.0.9}/daybetter_python/exceptions.py +6 -6
- daybetter_services_python-1.0.9/daybetter_services_python.egg-info/PKG-INFO +127 -0
- {daybetter_services_python-1.0.7 → daybetter_services_python-1.0.9}/daybetter_services_python.egg-info/SOURCES.txt +2 -2
- {daybetter_services_python-1.0.7 → daybetter_services_python-1.0.9}/pyproject.toml +9 -5
- daybetter_services_python-1.0.9/tests/test_placeholder.py +7 -0
- daybetter_services_python-1.0.7/PKG-INFO +0 -99
- daybetter_services_python-1.0.7/README.md +0 -59
- daybetter_services_python-1.0.7/daybetter_services_python.egg-info/PKG-INFO +0 -99
- daybetter_services_python-1.0.7/setup.py +0 -38
- {daybetter_services_python-1.0.7 → daybetter_services_python-1.0.9}/LICENSE +0 -0
- {daybetter_services_python-1.0.7 → daybetter_services_python-1.0.9}/daybetter_python/py.typed +0 -0
- {daybetter_services_python-1.0.7 → daybetter_services_python-1.0.9}/daybetter_services_python.egg-info/dependency_links.txt +0 -0
- {daybetter_services_python-1.0.7 → daybetter_services_python-1.0.9}/daybetter_services_python.egg-info/requires.txt +0 -0
- {daybetter_services_python-1.0.7 → daybetter_services_python-1.0.9}/daybetter_services_python.egg-info/top_level.txt +0 -0
- {daybetter_services_python-1.0.7 → daybetter_services_python-1.0.9}/setup.cfg +0 -0
|
@@ -0,0 +1,127 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: daybetter-services-python
|
|
3
|
+
Version: 1.0.9
|
|
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-services-python
|
|
9
|
+
Project-URL: Documentation, https://github.com/THDayBetter/daybetter-services-python#readme
|
|
10
|
+
Project-URL: Repository, https://github.com/THDayBetter/daybetter-services-python.git
|
|
11
|
+
Project-URL: Bug Tracker, https://github.com/THDayBetter/daybetter-services-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.7 → daybetter_services_python-1.0.9}/daybetter_python/client.py
RENAMED
|
@@ -1,61 +1,61 @@
|
|
|
1
1
|
"""DayBetter API client."""
|
|
2
2
|
|
|
3
|
-
import aiohttp
|
|
4
3
|
import logging
|
|
5
4
|
from typing import Any, Dict, List, Optional, Tuple, Type
|
|
6
5
|
|
|
7
|
-
|
|
6
|
+
import aiohttp
|
|
7
|
+
|
|
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
|
-
|
|
58
|
+
|
|
59
59
|
async def __aexit__(
|
|
60
60
|
self,
|
|
61
61
|
exc_type: Optional[Type[BaseException]],
|
|
@@ -63,26 +63,26 @@ class DayBetterClient:
|
|
|
63
63
|
exc_tb: Optional[Any],
|
|
64
64
|
) -> None:
|
|
65
65
|
"""Async context manager exit."""
|
|
66
|
-
if self._session:
|
|
66
|
+
if self._own_session and self._session is not None:
|
|
67
67
|
await self._session.close()
|
|
68
68
|
self._session = None
|
|
69
|
-
|
|
69
|
+
|
|
70
70
|
def _get_session(self) -> aiohttp.ClientSession:
|
|
71
71
|
"""Get or create aiohttp session."""
|
|
72
|
-
if
|
|
72
|
+
if self._session is None:
|
|
73
73
|
self._session = aiohttp.ClientSession()
|
|
74
74
|
return self._session
|
|
75
|
-
|
|
75
|
+
|
|
76
76
|
def _get_headers(self) -> Dict[str, str]:
|
|
77
77
|
"""Get request headers."""
|
|
78
78
|
return {"Authorization": f"Bearer {self.token}"}
|
|
79
|
-
|
|
79
|
+
|
|
80
80
|
async def fetch_devices(self) -> List[Dict[str, Any]]:
|
|
81
81
|
"""Fetch devices from API.
|
|
82
|
-
|
|
82
|
+
|
|
83
83
|
Returns:
|
|
84
84
|
List of device dictionaries
|
|
85
|
-
|
|
85
|
+
|
|
86
86
|
Raises:
|
|
87
87
|
AuthenticationError: If authentication fails
|
|
88
88
|
APIError: If API request fails
|
|
@@ -91,7 +91,7 @@ class DayBetterClient:
|
|
|
91
91
|
session = self._get_session()
|
|
92
92
|
url = f"{self.base_url}hass/devices"
|
|
93
93
|
headers = self._get_headers()
|
|
94
|
-
|
|
94
|
+
|
|
95
95
|
async with session.post(url, headers=headers) as resp:
|
|
96
96
|
if resp.status == 200:
|
|
97
97
|
data = await resp.json()
|
|
@@ -113,13 +113,13 @@ class DayBetterClient:
|
|
|
113
113
|
except Exception as e:
|
|
114
114
|
_LOGGER.exception("Exception while fetching devices: %s", e)
|
|
115
115
|
raise DayBetterError(f"Unexpected error: {e}")
|
|
116
|
-
|
|
116
|
+
|
|
117
117
|
async def fetch_pids(self) -> Dict[str, Any]:
|
|
118
118
|
"""Fetch device type PIDs.
|
|
119
|
-
|
|
119
|
+
|
|
120
120
|
Returns:
|
|
121
121
|
Dictionary of device type PIDs
|
|
122
|
-
|
|
122
|
+
|
|
123
123
|
Raises:
|
|
124
124
|
AuthenticationError: If authentication fails
|
|
125
125
|
APIError: If API request fails
|
|
@@ -128,7 +128,7 @@ class DayBetterClient:
|
|
|
128
128
|
session = self._get_session()
|
|
129
129
|
url = f"{self.base_url}hass/pids"
|
|
130
130
|
headers = self._get_headers()
|
|
131
|
-
|
|
131
|
+
|
|
132
132
|
async with session.post(url, headers=headers) as resp:
|
|
133
133
|
if resp.status == 200:
|
|
134
134
|
data = await resp.json()
|
|
@@ -148,7 +148,7 @@ class DayBetterClient:
|
|
|
148
148
|
except Exception as e:
|
|
149
149
|
_LOGGER.exception("Exception while fetching PIDs: %s", e)
|
|
150
150
|
raise DayBetterError(f"Unexpected error: {e}")
|
|
151
|
-
|
|
151
|
+
|
|
152
152
|
async def control_device(
|
|
153
153
|
self,
|
|
154
154
|
device_name: str,
|
|
@@ -158,17 +158,17 @@ class DayBetterClient:
|
|
|
158
158
|
color_temp: Optional[int] = None,
|
|
159
159
|
) -> Dict[str, Any]:
|
|
160
160
|
"""Control a device.
|
|
161
|
-
|
|
161
|
+
|
|
162
162
|
Args:
|
|
163
163
|
device_name: Name of the device to control
|
|
164
164
|
action: Switch action (True/False)
|
|
165
165
|
brightness: Brightness value (0-255)
|
|
166
166
|
hs_color: Hue and saturation tuple (hue, saturation)
|
|
167
167
|
color_temp: Color temperature in mireds
|
|
168
|
-
|
|
168
|
+
|
|
169
169
|
Returns:
|
|
170
170
|
Control result dictionary
|
|
171
|
-
|
|
171
|
+
|
|
172
172
|
Raises:
|
|
173
173
|
AuthenticationError: If authentication fails
|
|
174
174
|
APIError: If API request fails
|
|
@@ -176,10 +176,8 @@ class DayBetterClient:
|
|
|
176
176
|
session = self._get_session()
|
|
177
177
|
url = f"{self.base_url}hass/control"
|
|
178
178
|
headers = self._get_headers()
|
|
179
|
-
|
|
180
|
-
# Priority: color temperature > color > brightness > switch
|
|
179
|
+
|
|
181
180
|
if color_temp is not None:
|
|
182
|
-
# Convert mireds to Kelvin
|
|
183
181
|
kelvin = int(1000000 / color_temp)
|
|
184
182
|
payload = {
|
|
185
183
|
"deviceName": device_name,
|
|
@@ -198,18 +196,17 @@ class DayBetterClient:
|
|
|
198
196
|
}
|
|
199
197
|
elif brightness is not None:
|
|
200
198
|
payload = {
|
|
201
|
-
"deviceName": device_name,
|
|
202
|
-
"type": 2,
|
|
199
|
+
"deviceName": device_name,
|
|
200
|
+
"type": 2,
|
|
203
201
|
"brightness": brightness
|
|
204
202
|
}
|
|
205
203
|
else:
|
|
206
|
-
# Type 1 control switch is used by default
|
|
207
204
|
payload = {
|
|
208
|
-
"deviceName": device_name,
|
|
209
|
-
"type": 1,
|
|
205
|
+
"deviceName": device_name,
|
|
206
|
+
"type": 1,
|
|
210
207
|
"on": action
|
|
211
208
|
}
|
|
212
|
-
|
|
209
|
+
|
|
213
210
|
try:
|
|
214
211
|
async with session.post(url, headers=headers, json=payload) as resp:
|
|
215
212
|
if resp.status == 200:
|
|
@@ -222,7 +219,7 @@ class DayBetterClient:
|
|
|
222
219
|
else:
|
|
223
220
|
error_text = await resp.text()
|
|
224
221
|
_LOGGER.error(
|
|
225
|
-
"Failed to control device %s: HTTP %d - %s",
|
|
222
|
+
"Failed to control device %s: HTTP %d - %s",
|
|
226
223
|
device_name, resp.status, error_text
|
|
227
224
|
)
|
|
228
225
|
raise APIError(f"API error {resp.status}: {error_text}")
|
|
@@ -236,13 +233,13 @@ class DayBetterClient:
|
|
|
236
233
|
"Exception while controlling device %s: %s", device_name, e
|
|
237
234
|
)
|
|
238
235
|
raise DayBetterError(f"Unexpected error: {e}")
|
|
239
|
-
|
|
236
|
+
|
|
240
237
|
async def fetch_mqtt_config(self) -> Dict[str, Any]:
|
|
241
238
|
"""Fetch MQTT connection configuration.
|
|
242
|
-
|
|
239
|
+
|
|
243
240
|
Returns:
|
|
244
241
|
MQTT configuration dictionary
|
|
245
|
-
|
|
242
|
+
|
|
246
243
|
Raises:
|
|
247
244
|
AuthenticationError: If authentication fails
|
|
248
245
|
APIError: If API request fails
|
|
@@ -251,11 +248,11 @@ class DayBetterClient:
|
|
|
251
248
|
url = f"{self.base_url}hass/cert"
|
|
252
249
|
headers = self._get_headers()
|
|
253
250
|
_LOGGER.debug("Requesting MQTT configuration URL: %s", url)
|
|
254
|
-
|
|
251
|
+
|
|
255
252
|
try:
|
|
256
253
|
async with session.post(url, headers=headers) as resp:
|
|
257
254
|
_LOGGER.debug("MQTT configuration API response status: %d", resp.status)
|
|
258
|
-
|
|
255
|
+
|
|
259
256
|
if resp.status == 200:
|
|
260
257
|
data = await resp.json()
|
|
261
258
|
_LOGGER.debug("MQTT configuration API raw response: %s", data)
|
|
@@ -275,10 +272,10 @@ class DayBetterClient:
|
|
|
275
272
|
except Exception as e:
|
|
276
273
|
_LOGGER.exception("Exception while fetching MQTT config: %s", e)
|
|
277
274
|
raise DayBetterError(f"Unexpected error: {e}")
|
|
278
|
-
|
|
275
|
+
|
|
279
276
|
async def fetch_device_statuses(self) -> List[Dict[str, Any]]:
|
|
280
277
|
"""Fetch statuses for all devices.
|
|
281
|
-
|
|
278
|
+
|
|
282
279
|
Returns:
|
|
283
280
|
List of device status dictionaries. Example item:
|
|
284
281
|
{
|
|
@@ -289,7 +286,7 @@ class DayBetterClient:
|
|
|
289
286
|
"humi": int,
|
|
290
287
|
"bettery": int
|
|
291
288
|
}
|
|
292
|
-
|
|
289
|
+
|
|
293
290
|
Raises:
|
|
294
291
|
AuthenticationError: If authentication fails
|
|
295
292
|
APIError: If API request fails
|
|
@@ -298,12 +295,11 @@ class DayBetterClient:
|
|
|
298
295
|
session = self._get_session()
|
|
299
296
|
url = f"{self.base_url}hass/status"
|
|
300
297
|
headers = self._get_headers()
|
|
301
|
-
|
|
298
|
+
|
|
302
299
|
async with session.post(url, headers=headers) as resp:
|
|
303
300
|
if resp.status == 200:
|
|
304
301
|
data = await resp.json()
|
|
305
302
|
self._auth_valid = True
|
|
306
|
-
# API expected to return { "data": [...] }
|
|
307
303
|
return data.get("data", [])
|
|
308
304
|
elif resp.status == 401:
|
|
309
305
|
_LOGGER.error("Authentication failed - token may be expired")
|
|
@@ -319,21 +315,19 @@ class DayBetterClient:
|
|
|
319
315
|
except Exception as e:
|
|
320
316
|
_LOGGER.exception("Exception while fetching device statuses: %s", e)
|
|
321
317
|
raise DayBetterError(f"Unexpected error: {e}")
|
|
322
|
-
|
|
318
|
+
|
|
323
319
|
async def integrate(self, hass_code: str) -> Dict[str, Any]:
|
|
324
320
|
"""Integrate with Home Assistant using hassCode.
|
|
325
|
-
|
|
321
|
+
|
|
326
322
|
Args:
|
|
327
323
|
hass_code: Home Assistant integration code from APP
|
|
328
|
-
|
|
324
|
+
|
|
329
325
|
Returns:
|
|
330
326
|
Integration result dictionary
|
|
331
|
-
|
|
327
|
+
|
|
332
328
|
Raises:
|
|
333
329
|
APIError: If API request fails
|
|
334
330
|
"""
|
|
335
|
-
# 根据 hass_code 动态更新 base_url(如果之前没有明确指定)
|
|
336
|
-
# 如果 hass_code 以 "db-" 开头,切换到正式环境
|
|
337
331
|
if hass_code.startswith("db-") and self.base_url != self.PROD_BASE_URL:
|
|
338
332
|
old_url = self.base_url
|
|
339
333
|
self.base_url = self.PROD_BASE_URL
|
|
@@ -342,19 +336,18 @@ class DayBetterClient:
|
|
|
342
336
|
"URL changed from %s to %s", old_url, self.base_url
|
|
343
337
|
)
|
|
344
338
|
elif not hass_code.startswith("db-") and self.base_url != self.TEST_BASE_URL:
|
|
345
|
-
# 如果 hass_code 不以 "db-" 开头,且当前不是测试环境,切换到测试环境
|
|
346
339
|
old_url = self.base_url
|
|
347
340
|
self.base_url = self.TEST_BASE_URL
|
|
348
341
|
_LOGGER.info(
|
|
349
342
|
"Switching to test environment based on hass_code. "
|
|
350
343
|
"URL changed from %s to %s", old_url, self.base_url
|
|
351
344
|
)
|
|
352
|
-
|
|
345
|
+
|
|
353
346
|
try:
|
|
354
347
|
session = self._get_session()
|
|
355
348
|
url = f"{self.base_url}hass/integrate"
|
|
356
349
|
payload = {"hassCode": hass_code}
|
|
357
|
-
|
|
350
|
+
|
|
358
351
|
async with session.post(url, json=payload) as resp:
|
|
359
352
|
if resp.status == 200:
|
|
360
353
|
data = await resp.json()
|
|
@@ -370,23 +363,23 @@ class DayBetterClient:
|
|
|
370
363
|
except Exception as e:
|
|
371
364
|
_LOGGER.exception("Exception while integrating: %s", e)
|
|
372
365
|
raise DayBetterError(f"Unexpected error: {e}")
|
|
373
|
-
|
|
366
|
+
|
|
374
367
|
@property
|
|
375
368
|
def is_authenticated(self) -> bool:
|
|
376
369
|
"""Check if the API client is authenticated."""
|
|
377
370
|
return self._auth_valid
|
|
378
|
-
|
|
371
|
+
|
|
379
372
|
def filter_sensor_devices(
|
|
380
373
|
self,
|
|
381
374
|
devices: List[Dict[str, Any]],
|
|
382
375
|
pids: Dict[str, Any],
|
|
383
376
|
) -> List[Dict[str, Any]]:
|
|
384
377
|
"""Filter devices to only include sensors based on PID.
|
|
385
|
-
|
|
378
|
+
|
|
386
379
|
Args:
|
|
387
380
|
devices: List of all devices
|
|
388
381
|
pids: Dictionary containing device type PIDs
|
|
389
|
-
|
|
382
|
+
|
|
390
383
|
Returns:
|
|
391
384
|
List of sensor devices only
|
|
392
385
|
"""
|
|
@@ -401,18 +394,18 @@ class DayBetterClient:
|
|
|
401
394
|
for device in devices
|
|
402
395
|
if device.get("deviceMoldPid", "") in sensor_pids
|
|
403
396
|
]
|
|
404
|
-
|
|
397
|
+
|
|
405
398
|
def merge_device_status(
|
|
406
399
|
self,
|
|
407
400
|
devices: List[Dict[str, Any]],
|
|
408
401
|
statuses: List[Dict[str, Any]],
|
|
409
402
|
) -> List[Dict[str, Any]]:
|
|
410
403
|
"""Merge device info with status info.
|
|
411
|
-
|
|
404
|
+
|
|
412
405
|
Args:
|
|
413
406
|
devices: List of device info dictionaries
|
|
414
407
|
statuses: List of device status dictionaries
|
|
415
|
-
|
|
408
|
+
|
|
416
409
|
Returns:
|
|
417
410
|
List of merged device dictionaries
|
|
418
411
|
"""
|
|
@@ -429,38 +422,34 @@ class DayBetterClient:
|
|
|
429
422
|
merged.append(merged_device)
|
|
430
423
|
|
|
431
424
|
return merged
|
|
432
|
-
|
|
425
|
+
|
|
433
426
|
async def fetch_sensor_data(self) -> List[Dict[str, Any]]:
|
|
434
427
|
"""Fetch and process sensor data in one call.
|
|
435
|
-
|
|
428
|
+
|
|
436
429
|
This method fetches device statuses, devices list, and PIDs,
|
|
437
430
|
filters for sensor devices, and merges the data.
|
|
438
|
-
|
|
431
|
+
|
|
439
432
|
Returns:
|
|
440
433
|
List of sensor devices with merged status data
|
|
441
|
-
|
|
434
|
+
|
|
442
435
|
Raises:
|
|
443
436
|
AuthenticationError: If authentication fails
|
|
444
437
|
APIError: If API request fails
|
|
445
438
|
"""
|
|
446
|
-
# Fetch current statuses
|
|
447
439
|
statuses = await self.fetch_device_statuses()
|
|
448
|
-
|
|
449
|
-
# Fetch devices and PIDs if not cached
|
|
440
|
+
|
|
450
441
|
if not self._devices or not self._pids:
|
|
451
442
|
self._devices = await self.fetch_devices()
|
|
452
443
|
self._pids = await self.fetch_pids()
|
|
453
|
-
|
|
454
|
-
# Filter to sensor devices only
|
|
444
|
+
|
|
455
445
|
sensor_devices = self.filter_sensor_devices(self._devices, self._pids)
|
|
456
|
-
|
|
457
|
-
# Merge with current status
|
|
446
|
+
|
|
458
447
|
merged = self.merge_device_status(sensor_devices, statuses)
|
|
459
448
|
_LOGGER.debug("Fetched %d sensor devices", len(merged))
|
|
460
449
|
return merged
|
|
461
|
-
|
|
450
|
+
|
|
462
451
|
async def close(self) -> None:
|
|
463
|
-
"""Close the client session."""
|
|
464
|
-
if self._session:
|
|
452
|
+
"""Close the client session if this client created it."""
|
|
453
|
+
if self._own_session and self._session is not None:
|
|
465
454
|
await self._session.close()
|
|
466
455
|
self._session = None
|
{daybetter_services_python-1.0.7 → daybetter_services_python-1.0.9}/daybetter_python/exceptions.py
RENAMED
|
@@ -3,10 +3,10 @@
|
|
|
3
3
|
|
|
4
4
|
class DayBetterError(Exception):
|
|
5
5
|
"""Base exception for DayBetter client."""
|
|
6
|
-
|
|
6
|
+
|
|
7
7
|
def __init__(self, message: str) -> None:
|
|
8
8
|
"""Initialize the exception.
|
|
9
|
-
|
|
9
|
+
|
|
10
10
|
Args:
|
|
11
11
|
message: Error message
|
|
12
12
|
"""
|
|
@@ -16,10 +16,10 @@ class DayBetterError(Exception):
|
|
|
16
16
|
|
|
17
17
|
class AuthenticationError(DayBetterError):
|
|
18
18
|
"""Authentication failed."""
|
|
19
|
-
|
|
19
|
+
|
|
20
20
|
def __init__(self, message: str = "Authentication failed") -> None:
|
|
21
21
|
"""Initialize the authentication error.
|
|
22
|
-
|
|
22
|
+
|
|
23
23
|
Args:
|
|
24
24
|
message: Error message
|
|
25
25
|
"""
|
|
@@ -28,10 +28,10 @@ class AuthenticationError(DayBetterError):
|
|
|
28
28
|
|
|
29
29
|
class APIError(DayBetterError):
|
|
30
30
|
"""API request failed."""
|
|
31
|
-
|
|
31
|
+
|
|
32
32
|
def __init__(self, message: str) -> None:
|
|
33
33
|
"""Initialize the API error.
|
|
34
|
-
|
|
34
|
+
|
|
35
35
|
Args:
|
|
36
36
|
message: Error message
|
|
37
37
|
"""
|
|
@@ -0,0 +1,127 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: daybetter-services-python
|
|
3
|
+
Version: 1.0.9
|
|
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-services-python
|
|
9
|
+
Project-URL: Documentation, https://github.com/THDayBetter/daybetter-services-python#readme
|
|
10
|
+
Project-URL: Repository, https://github.com/THDayBetter/daybetter-services-python.git
|
|
11
|
+
Project-URL: Bug Tracker, https://github.com/THDayBetter/daybetter-services-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,7 +1,6 @@
|
|
|
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
|
|
@@ -10,4 +9,5 @@ daybetter_services_python.egg-info/PKG-INFO
|
|
|
10
9
|
daybetter_services_python.egg-info/SOURCES.txt
|
|
11
10
|
daybetter_services_python.egg-info/dependency_links.txt
|
|
12
11
|
daybetter_services_python.egg-info/requires.txt
|
|
13
|
-
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.9"
|
|
8
8
|
description = "Python client for DayBetter devices and services"
|
|
9
9
|
readme = "README.md"
|
|
10
10
|
license = "MIT"
|
|
@@ -33,10 +33,10 @@ dependencies = [
|
|
|
33
33
|
]
|
|
34
34
|
|
|
35
35
|
[project.urls]
|
|
36
|
-
Homepage = "https://github.com/THDayBetter/daybetter-python"
|
|
37
|
-
Documentation = "https://github.com/THDayBetter/daybetter-python#readme"
|
|
38
|
-
Repository = "https://github.com/THDayBetter/daybetter-python.git"
|
|
39
|
-
"Bug Tracker" = "https://github.com/THDayBetter/daybetter-python/issues"
|
|
36
|
+
Homepage = "https://github.com/THDayBetter/daybetter-services-python"
|
|
37
|
+
Documentation = "https://github.com/THDayBetter/daybetter-services-python#readme"
|
|
38
|
+
Repository = "https://github.com/THDayBetter/daybetter-services-python.git"
|
|
39
|
+
"Bug Tracker" = "https://github.com/THDayBetter/daybetter-services-python/issues"
|
|
40
40
|
|
|
41
41
|
[project.optional-dependencies]
|
|
42
42
|
dev = [
|
|
@@ -57,5 +57,9 @@ target-version = ['py38']
|
|
|
57
57
|
profile = "black"
|
|
58
58
|
line_length = 88
|
|
59
59
|
|
|
60
|
+
[tool.flake8]
|
|
61
|
+
max-line-length = 100
|
|
62
|
+
extend-ignore = ["W503"]
|
|
63
|
+
|
|
60
64
|
[tool.setuptools.package-data]
|
|
61
65
|
daybetter_python = ["py.typed"]
|
|
@@ -1,99 +0,0 @@
|
|
|
1
|
-
Metadata-Version: 2.4
|
|
2
|
-
Name: daybetter-services-python
|
|
3
|
-
Version: 1.0.7
|
|
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
|
-
Classifier: Typing :: Typed
|
|
25
|
-
Requires-Python: >=3.8
|
|
26
|
-
Description-Content-Type: text/markdown
|
|
27
|
-
License-File: LICENSE
|
|
28
|
-
Requires-Dist: aiohttp>=3.8.0
|
|
29
|
-
Provides-Extra: dev
|
|
30
|
-
Requires-Dist: pytest>=6.0; extra == "dev"
|
|
31
|
-
Requires-Dist: pytest-asyncio; extra == "dev"
|
|
32
|
-
Requires-Dist: black; extra == "dev"
|
|
33
|
-
Requires-Dist: isort; extra == "dev"
|
|
34
|
-
Requires-Dist: flake8; extra == "dev"
|
|
35
|
-
Requires-Dist: mypy; extra == "dev"
|
|
36
|
-
Dynamic: author
|
|
37
|
-
Dynamic: home-page
|
|
38
|
-
Dynamic: license-file
|
|
39
|
-
Dynamic: requires-python
|
|
40
|
-
|
|
41
|
-
# DayBetter Python Client
|
|
42
|
-
|
|
43
|
-
A Python client library for interacting with DayBetter devices and services.
|
|
44
|
-
|
|
45
|
-
## Features
|
|
46
|
-
|
|
47
|
-
- Device management and control
|
|
48
|
-
- MQTT configuration retrieval
|
|
49
|
-
- Authentication handling
|
|
50
|
-
- Async/await support
|
|
51
|
-
|
|
52
|
-
## Installation
|
|
53
|
-
|
|
54
|
-
```bash
|
|
55
|
-
pip install daybetter-services-python
|
|
56
|
-
```
|
|
57
|
-
|
|
58
|
-
## Usage
|
|
59
|
-
|
|
60
|
-
```python
|
|
61
|
-
import asyncio
|
|
62
|
-
from daybetter_python import DayBetterClient
|
|
63
|
-
|
|
64
|
-
async def main():
|
|
65
|
-
async with DayBetterClient(token="your_token") as client:
|
|
66
|
-
# Fetch devices
|
|
67
|
-
devices = await client.fetch_devices()
|
|
68
|
-
print(f"Found {len(devices)} devices")
|
|
69
|
-
|
|
70
|
-
# Control a device
|
|
71
|
-
result = await client.control_device(
|
|
72
|
-
device_name="device_001",
|
|
73
|
-
action=True,
|
|
74
|
-
brightness=80
|
|
75
|
-
)
|
|
76
|
-
print(f"Control result: {result}")
|
|
77
|
-
|
|
78
|
-
if __name__ == "__main__":
|
|
79
|
-
asyncio.run(main())
|
|
80
|
-
```
|
|
81
|
-
|
|
82
|
-
## API Reference
|
|
83
|
-
|
|
84
|
-
### DayBetterClient
|
|
85
|
-
|
|
86
|
-
#### Methods
|
|
87
|
-
|
|
88
|
-
- `fetch_devices()`: Get list of devices
|
|
89
|
-
- `fetch_pids()`: Get device type PIDs
|
|
90
|
-
- `control_device(device_name, action, brightness, hs_color, color_temp)`: Control a device
|
|
91
|
-
- `fetch_mqtt_config()`: Get MQTT configuration
|
|
92
|
-
|
|
93
|
-
## License
|
|
94
|
-
|
|
95
|
-
MIT License
|
|
96
|
-
|
|
97
|
-
## Contributing
|
|
98
|
-
|
|
99
|
-
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,99 +0,0 @@
|
|
|
1
|
-
Metadata-Version: 2.4
|
|
2
|
-
Name: daybetter-services-python
|
|
3
|
-
Version: 1.0.7
|
|
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
|
-
Classifier: Typing :: Typed
|
|
25
|
-
Requires-Python: >=3.8
|
|
26
|
-
Description-Content-Type: text/markdown
|
|
27
|
-
License-File: LICENSE
|
|
28
|
-
Requires-Dist: aiohttp>=3.8.0
|
|
29
|
-
Provides-Extra: dev
|
|
30
|
-
Requires-Dist: pytest>=6.0; extra == "dev"
|
|
31
|
-
Requires-Dist: pytest-asyncio; extra == "dev"
|
|
32
|
-
Requires-Dist: black; extra == "dev"
|
|
33
|
-
Requires-Dist: isort; extra == "dev"
|
|
34
|
-
Requires-Dist: flake8; extra == "dev"
|
|
35
|
-
Requires-Dist: mypy; extra == "dev"
|
|
36
|
-
Dynamic: author
|
|
37
|
-
Dynamic: home-page
|
|
38
|
-
Dynamic: license-file
|
|
39
|
-
Dynamic: requires-python
|
|
40
|
-
|
|
41
|
-
# DayBetter Python Client
|
|
42
|
-
|
|
43
|
-
A Python client library for interacting with DayBetter devices and services.
|
|
44
|
-
|
|
45
|
-
## Features
|
|
46
|
-
|
|
47
|
-
- Device management and control
|
|
48
|
-
- MQTT configuration retrieval
|
|
49
|
-
- Authentication handling
|
|
50
|
-
- Async/await support
|
|
51
|
-
|
|
52
|
-
## Installation
|
|
53
|
-
|
|
54
|
-
```bash
|
|
55
|
-
pip install daybetter-services-python
|
|
56
|
-
```
|
|
57
|
-
|
|
58
|
-
## Usage
|
|
59
|
-
|
|
60
|
-
```python
|
|
61
|
-
import asyncio
|
|
62
|
-
from daybetter_python import DayBetterClient
|
|
63
|
-
|
|
64
|
-
async def main():
|
|
65
|
-
async with DayBetterClient(token="your_token") as client:
|
|
66
|
-
# Fetch devices
|
|
67
|
-
devices = await client.fetch_devices()
|
|
68
|
-
print(f"Found {len(devices)} devices")
|
|
69
|
-
|
|
70
|
-
# Control a device
|
|
71
|
-
result = await client.control_device(
|
|
72
|
-
device_name="device_001",
|
|
73
|
-
action=True,
|
|
74
|
-
brightness=80
|
|
75
|
-
)
|
|
76
|
-
print(f"Control result: {result}")
|
|
77
|
-
|
|
78
|
-
if __name__ == "__main__":
|
|
79
|
-
asyncio.run(main())
|
|
80
|
-
```
|
|
81
|
-
|
|
82
|
-
## API Reference
|
|
83
|
-
|
|
84
|
-
### DayBetterClient
|
|
85
|
-
|
|
86
|
-
#### Methods
|
|
87
|
-
|
|
88
|
-
- `fetch_devices()`: Get list of devices
|
|
89
|
-
- `fetch_pids()`: Get device type PIDs
|
|
90
|
-
- `control_device(device_name, action, brightness, hs_color, color_temp)`: Control a device
|
|
91
|
-
- `fetch_mqtt_config()`: Get MQTT configuration
|
|
92
|
-
|
|
93
|
-
## License
|
|
94
|
-
|
|
95
|
-
MIT License
|
|
96
|
-
|
|
97
|
-
## Contributing
|
|
98
|
-
|
|
99
|
-
Contributions are welcome! Please feel free to submit a Pull Request.
|
|
@@ -1,38 +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.7",
|
|
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
|
-
package_data={
|
|
17
|
-
"daybetter_python": ["py.typed"],
|
|
18
|
-
},
|
|
19
|
-
classifiers=[
|
|
20
|
-
"Development Status :: 4 - Beta",
|
|
21
|
-
"Intended Audience :: Developers",
|
|
22
|
-
"Operating System :: OS Independent",
|
|
23
|
-
"Programming Language :: Python :: 3",
|
|
24
|
-
"Programming Language :: Python :: 3.8",
|
|
25
|
-
"Programming Language :: Python :: 3.9",
|
|
26
|
-
"Programming Language :: Python :: 3.10",
|
|
27
|
-
"Programming Language :: Python :: 3.11",
|
|
28
|
-
"Programming Language :: Python :: 3.12",
|
|
29
|
-
"Typing :: Typed",
|
|
30
|
-
],
|
|
31
|
-
python_requires=">=3.8",
|
|
32
|
-
install_requires=["aiohttp>=3.8.0"],
|
|
33
|
-
keywords="daybetter, iot, home automation, mqtt",
|
|
34
|
-
project_urls={
|
|
35
|
-
"Bug Reports": "https://github.com/THDayBetter/daybetter-python/issues",
|
|
36
|
-
"Source": "https://github.com/THDayBetter/daybetter-python",
|
|
37
|
-
},
|
|
38
|
-
)
|
|
File without changes
|
{daybetter_services_python-1.0.7 → daybetter_services_python-1.0.9}/daybetter_python/py.typed
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|