Skip to content

Commit 419f84d

Browse files
authored
Add night lamps support (#30)
1 parent fe89113 commit 419f84d

File tree

6 files changed

+132
-4
lines changed

6 files changed

+132
-4
lines changed

.gitignore

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,4 +7,7 @@ __pycache__
77
/bin
88
/lib
99
/include
10-
/pyvenv.cfg
10+
/pyvenv.cfg
11+
.env.local
12+
venv/
13+
.idea

custom_components/sberdevices/light.py

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,8 @@ def get_color_temp_range(device_type: str) -> (int, int):
3030
return 2000, 6500
3131
case "bulb": # Sber A60 bulb
3232
return 2700, 6500
33+
case "night_lamp": # Sber night lamp
34+
return 2700, 6500
3335
case _:
3436
return 2700, 6500
3537

@@ -43,15 +45,15 @@ async def async_setup_entry(
4345
) -> None:
4446
home: HomeAPI = hass.data[DOMAIN][entry.entry_id]["home"]
4547
await home.update_devices_cache()
48+
light_types = ("bulb", "ledstrip", "night_lamp")
4649
async_add_entities(
4750
[
4851
SberLightEntity(
4952
DeviceAPI(home, device["id"]),
50-
"ledstrip" if "ledstrip" in device["image_set_type"] else "bulb",
53+
next(t for t in light_types if t in device["image_set_type"]),
5154
)
5255
for device in home.get_cached_devices().values()
53-
if "bulb" in device["image_set_type"]
54-
or "ledstrip" in device["image_set_type"] # TODO: lutiy kostyl'
56+
if any(t in device["image_set_type"] for t in light_types)
5557
]
5658
)
5759

pytest.ini

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
[pytest]
2+
pythonpath = .
3+
asyncio_mode = auto

requirements_dev.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
homeassistant

scripts/get_token.py

Lines changed: 56 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,56 @@
1+
"""
2+
Script to obtain SBER_ACCESS_TOKEN via OAuth2 flow.
3+
4+
Run:
5+
python scripts/get_token.py
6+
"""
7+
8+
import asyncio
9+
import json
10+
import sys
11+
import os
12+
13+
sys.path.insert(0, os.path.join(os.path.dirname(__file__), ".."))
14+
15+
from custom_components.sberdevices.api import SberAPI
16+
17+
18+
async def main():
19+
sber = SberAPI()
20+
21+
url = sber.create_authorization_url()
22+
print("1. Откройте эту ссылку в браузере и авторизуйтесь в Сбербанк Онлайн:\n")
23+
print(url)
24+
print("\n2. После авторизации браузер перенаправит на URL вида:")
25+
print(" companionapp://host?code=...&state=...\n")
26+
print("3. Скопируйте ПОЛНЫЙ URL редиректа и вставьте сюда:\n")
27+
28+
redirect_url = input("URL: ").strip()
29+
30+
success = await sber.authorize_by_url(redirect_url)
31+
32+
if not success:
33+
print("\nОшибка авторизации. Проверьте URL.")
34+
return
35+
36+
token = sber.token
37+
38+
env_lines = [
39+
f'SBER_ACCESS_TOKEN={token["access_token"]}',
40+
f'SBER_REFRESH_TOKEN={token.get("refresh_token", "")}',
41+
f'SBER_TOKEN_TYPE={token.get("token_type", "Bearer")}',
42+
f'SBER_EXPIRES_AT={int(token.get("expires_at", 0))}',
43+
]
44+
45+
env_path = os.path.join(os.path.dirname(__file__), "..", ".env.local")
46+
with open(env_path, "w") as f:
47+
f.write("\n".join(env_lines) + "\n")
48+
49+
print(f"\nУспешно! Токен сохранён в .env.local\n")
50+
for line in env_lines:
51+
print(f" {line}")
52+
print(f"\nПолный токен (JSON):\n{json.dumps(token, indent=2)}")
53+
54+
55+
if __name__ == "__main__":
56+
asyncio.run(main())

tests/test_real_api.py

Lines changed: 63 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,63 @@
1+
"""
2+
Test that calls the real SberDevices API.
3+
4+
Requires a valid OAuth token in .env.local (created by scripts/get_token.py)
5+
or in environment variables.
6+
7+
Run:
8+
pytest tests/test_real_api.py -s
9+
"""
10+
11+
import json
12+
import os
13+
from pathlib import Path
14+
15+
import pytest
16+
17+
from custom_components.sberdevices.api import HomeAPI, SberAPI
18+
19+
ENV_FILE = Path(__file__).resolve().parent.parent / ".env.local"
20+
21+
22+
def load_env_file():
23+
"""Load variables from .env.local if it exists."""
24+
if not ENV_FILE.exists():
25+
return
26+
for line in ENV_FILE.read_text().splitlines():
27+
line = line.strip()
28+
if not line or line.startswith("#"):
29+
continue
30+
key, _, value = line.partition("=")
31+
if key and _ and key not in os.environ:
32+
os.environ[key] = value
33+
34+
35+
def load_token() -> dict:
36+
load_env_file()
37+
38+
access_token = os.environ.get("SBER_ACCESS_TOKEN")
39+
refresh_token = os.environ.get("SBER_REFRESH_TOKEN")
40+
41+
if not access_token:
42+
pytest.skip("SBER_ACCESS_TOKEN not set — skipping real API test")
43+
44+
return {
45+
"access_token": access_token,
46+
"refresh_token": refresh_token or "",
47+
"token_type": os.environ.get("SBER_TOKEN_TYPE", "Bearer"),
48+
"expires_at": int(os.environ.get("SBER_EXPIRES_AT", "0")),
49+
}
50+
51+
52+
@pytest.mark.asyncio
53+
async def test_get_device_tree():
54+
"""Fetch real device tree from SberDevices API."""
55+
token = load_token()
56+
sber = SberAPI(token=token)
57+
home = HomeAPI(sber)
58+
59+
device_tree = await home.get_device_tree()
60+
61+
assert device_tree is not None
62+
assert "devices" in device_tree
63+
print(json.dumps(device_tree, indent=2, ensure_ascii=False))

0 commit comments

Comments
 (0)