Skip to content

Commit d1eba39

Browse files
committed
Fix support climate from YNDX-00571 #746
1 parent f4fc8af commit d1eba39

File tree

3 files changed

+283
-6
lines changed

3 files changed

+283
-6
lines changed

custom_components/yandex_station/core/entity.py

Lines changed: 7 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@
99
_LOGGER = logging.getLogger(__name__)
1010

1111

12-
def extract_instance(item: dict) -> str:
12+
def extract_instance(item: dict) -> str | None:
1313
if item["type"] == "devices.capabilities.on_off":
1414
return "on"
1515
if item["type"] == "devices.capabilities.lock":
@@ -22,17 +22,18 @@ def extract_instance(item: dict) -> str:
2222
def extract_parameters(items: list[dict]) -> dict:
2323
result = {}
2424
for item in items:
25-
instance = extract_instance(item)
26-
result[instance] = {"retrievable": item["retrievable"], **item["parameters"]}
25+
# skip none (unknown) instances
26+
if instance := extract_instance(item):
27+
result[instance] = {"retrievable": item["retrievable"], **item["parameters"]}
2728
return result
2829

2930

3031
def extract_state(items: list[dict]) -> dict:
3132
result = {}
3233
for item in items:
33-
instance = extract_instance(item)
34-
value = item["state"]["value"] if item["state"] else None
35-
result[instance] = value
34+
if instance := extract_instance(item):
35+
value = item["state"]["value"] if item["state"] else None
36+
result[instance] = value
3637
return result
3738

3839

tests/__init__.py

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,11 @@ def __init__(self):
3131

3232

3333
def update_ha_state(cls, device: dict, **kwargs) -> State:
34+
device.setdefault("id", "ID")
35+
device.setdefault("name", "NAME")
36+
device.setdefault("capabilities", [])
37+
device.setdefault("properties", [])
38+
3439
asyncio.get_running_loop = lambda: asyncio.new_event_loop()
3540

3641
entity: YandexEntity = cls(FakeQuasar(device), device, **kwargs)

tests/test_climate.py

Lines changed: 271 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1129,3 +1129,274 @@ def test_rusclimate():
11291129
"target_temp_step": 1,
11301130
"temperature": 25,
11311131
}
1132+
1133+
1134+
def test_issue744():
1135+
device = {
1136+
"name": "Термостат Гостиная",
1137+
"type": "devices.types.thermostat",
1138+
"capabilities": [
1139+
{
1140+
"reportable": true,
1141+
"retrievable": true,
1142+
"type": "devices.capabilities.on_off",
1143+
"state": {"instance": "on", "value": true},
1144+
"parameters": {"split": false},
1145+
"can_be_deferred": true,
1146+
},
1147+
{
1148+
"reportable": true,
1149+
"retrievable": true,
1150+
"type": "devices.capabilities.range",
1151+
"state": {"instance": "temperature", "value": 29.5},
1152+
"parameters": {
1153+
"instance": "temperature",
1154+
"name": "температура",
1155+
"unit": "unit.temperature.celsius",
1156+
"random_access": true,
1157+
"looped": false,
1158+
"range": {"min": 5, "max": 30, "precision": 0.5},
1159+
},
1160+
},
1161+
{
1162+
"reportable": true,
1163+
"retrievable": true,
1164+
"type": "devices.capabilities.toggle",
1165+
"state": {"instance": "controls_locked", "value": false},
1166+
"parameters": {
1167+
"instance": "controls_locked",
1168+
"name": "блокировка управления",
1169+
},
1170+
},
1171+
{
1172+
"reportable": false,
1173+
"retrievable": false,
1174+
"type": "devices.capabilities.identify",
1175+
"state": null,
1176+
"parameters": {
1177+
"instance": "identify",
1178+
"time_range_sec": {"min": 0, "max": 65535, "precision": 0},
1179+
},
1180+
},
1181+
{
1182+
"reportable": true,
1183+
"retrievable": true,
1184+
"type": "devices.capabilities.auto_calibration",
1185+
"state": {
1186+
"value": {"state": "calibrated", "recalibration_enabled": true}
1187+
},
1188+
"parameters": {
1189+
"instance": "auto_calibration",
1190+
"not_allowed_recalibration": false,
1191+
},
1192+
},
1193+
{
1194+
"reportable": true,
1195+
"retrievable": true,
1196+
"type": "devices.capabilities.display_orientation",
1197+
"state": {"value": {"orientation": "vertical_reverse"}},
1198+
"parameters": {"instance": "display_orientation"},
1199+
},
1200+
{
1201+
"reportable": true,
1202+
"retrievable": true,
1203+
"type": "devices.capabilities.open_window_detection",
1204+
"state": {"value": {"enabled": true, "is_open": false}},
1205+
"parameters": {"instance": "open_window_detection"},
1206+
},
1207+
{
1208+
"reportable": true,
1209+
"retrievable": true,
1210+
"type": "devices.capabilities.antifreeze",
1211+
"state": {"value": {"enabled": true}},
1212+
"parameters": {"instance": "antifreeze"},
1213+
},
1214+
{
1215+
"reportable": true,
1216+
"retrievable": true,
1217+
"type": "devices.capabilities.antiscale",
1218+
"state": {"value": {"enabled": true}},
1219+
"parameters": {"instance": "antiscale"},
1220+
},
1221+
{
1222+
"reportable": true,
1223+
"retrievable": false,
1224+
"type": "devices.capabilities.zigbee_node",
1225+
"state": {
1226+
"value": {
1227+
"signal_quality": {"status": "good", "lqi": 109, "rssi": -63}
1228+
}
1229+
},
1230+
"parameters": {"type": "end_device"},
1231+
},
1232+
],
1233+
"properties": [
1234+
{
1235+
"type": "devices.properties.event",
1236+
"retrievable": false,
1237+
"reportable": true,
1238+
"parameters": {
1239+
"instance": "serviceability",
1240+
"name": "работоспособность устройства",
1241+
"events": [
1242+
{
1243+
"value": "short_or_open_circuit",
1244+
"name": "короткое замыкание или обрыв цепи датчика",
1245+
},
1246+
{
1247+
"value": "radiator_valve_blockage",
1248+
"name": "медленное движение или заклинивание штока",
1249+
},
1250+
{
1251+
"value": "radiator_valve_too_long",
1252+
"name": "шток слишком длинный",
1253+
},
1254+
{
1255+
"value": "radiator_valve_too_short",
1256+
"name": "шток слишком короткий",
1257+
},
1258+
{
1259+
"value": "radiator_valve_has_no_resistance",
1260+
"name": "отсутствует сопротивление клапана",
1261+
},
1262+
{
1263+
"value": "button_pressed_for_too_long",
1264+
"name": "слишком долгое удержание кнопки",
1265+
},
1266+
{"value": "normal", "name": "нормальное состояние устройства"},
1267+
],
1268+
},
1269+
"state": {
1270+
"instance": "serviceability",
1271+
"status": "normal",
1272+
"value": "normal",
1273+
},
1274+
"last_activated": "2026-02-12T04:55:33Z",
1275+
"last_updated": "2026-02-12T04:55:33Z",
1276+
},
1277+
{
1278+
"type": "devices.properties.float",
1279+
"retrievable": false,
1280+
"reportable": true,
1281+
"parameters": {
1282+
"instance": "temperature",
1283+
"name": "температура",
1284+
"unit": "unit.temperature.celsius",
1285+
"range": {"min": -273.15, "max": 327.67, "precision": 0.01},
1286+
},
1287+
"state": {"percent": null, "status": null, "value": 26.3},
1288+
"state_changed_at": "2026-02-12T09:11:14Z",
1289+
"last_updated": "2026-02-12T09:11:14Z",
1290+
},
1291+
{
1292+
"type": "devices.properties.float",
1293+
"retrievable": false,
1294+
"reportable": true,
1295+
"parameters": {
1296+
"instance": "battery_level",
1297+
"name": "уровень заряда",
1298+
"unit": "unit.percent",
1299+
},
1300+
"state": {"percent": 75, "status": "warning", "value": 75},
1301+
"trend": {"target": 0, "status": "normal"},
1302+
"state_changed_at": "2026-02-08T10:04:43Z",
1303+
"last_updated": "2026-02-12T06:25:03Z",
1304+
},
1305+
],
1306+
"parameters": {
1307+
"device_info": {
1308+
"manufacturer": "Yandex",
1309+
"model": "YNDX-00518",
1310+
},
1311+
},
1312+
}
1313+
state = update_ha_state(YandexClimate, device, config={})
1314+
assert state.state == "auto"
1315+
1316+
1317+
def test_issue746():
1318+
device = {
1319+
"name": "Кондиционер",
1320+
"type": "devices.types.thermostat.ac",
1321+
"capabilities": [
1322+
{
1323+
"reportable": true,
1324+
"retrievable": true,
1325+
"type": "devices.capabilities.on_off",
1326+
"state": {"instance": "on", "value": false},
1327+
"parameters": {"split": false},
1328+
"can_be_deferred": true,
1329+
},
1330+
{
1331+
"reportable": true,
1332+
"retrievable": true,
1333+
"type": "devices.capabilities.range",
1334+
"state": {"instance": "temperature", "value": 16},
1335+
"parameters": {
1336+
"instance": "temperature",
1337+
"name": "температура",
1338+
"unit": "unit.temperature.celsius",
1339+
"random_access": true,
1340+
"looped": false,
1341+
"range": {"min": 16, "max": 30, "precision": 1},
1342+
},
1343+
},
1344+
{
1345+
"reportable": true,
1346+
"retrievable": true,
1347+
"type": "devices.capabilities.mode",
1348+
"state": {"instance": "swing", "value": "low"},
1349+
"parameters": {
1350+
"instance": "swing",
1351+
"name": "направление воздуха",
1352+
"modes": [
1353+
{"value": "low", "name": "Низкая"},
1354+
{"value": "medium", "name": "Средняя"},
1355+
],
1356+
},
1357+
},
1358+
{
1359+
"reportable": true,
1360+
"retrievable": true,
1361+
"type": "devices.capabilities.mode",
1362+
"state": {"instance": "fan_speed", "value": "auto"},
1363+
"parameters": {
1364+
"instance": "fan_speed",
1365+
"name": "скорость вентиляции",
1366+
"modes": [
1367+
{"value": "auto", "name": "Авто"},
1368+
{"value": "low", "name": "Низкая"},
1369+
{"value": "medium", "name": "Средняя"},
1370+
{"value": "high", "name": "Высокая"},
1371+
],
1372+
},
1373+
},
1374+
{
1375+
"reportable": true,
1376+
"retrievable": true,
1377+
"type": "devices.capabilities.mode",
1378+
"state": {"instance": "thermostat", "value": "auto"},
1379+
"parameters": {
1380+
"instance": "thermostat",
1381+
"name": "термостат",
1382+
"modes": [
1383+
{"value": "cool", "name": "Охлаждение"},
1384+
{"value": "heat", "name": "Нагрев"},
1385+
{"value": "fan_only", "name": "Вентиляция"},
1386+
{"value": "dry", "name": "Осушение"},
1387+
{"value": "auto", "name": "Авто"},
1388+
],
1389+
},
1390+
},
1391+
{
1392+
"reportable": false,
1393+
"retrievable": false,
1394+
"type": "devices.capabilities.ir_remote",
1395+
"state": null,
1396+
"parameters": {},
1397+
},
1398+
],
1399+
}
1400+
1401+
state = update_ha_state(YandexClimate, device, config={})
1402+
assert state.state == "off"

0 commit comments

Comments
 (0)