1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150
|
"""Tests for sending values to the BSBLAN device."""
# pylint: disable=protected-access
# pylint: disable=redefined-outer-name
from __future__ import annotations
import json
import logging
from typing import TYPE_CHECKING, Any
import aiohttp
import pytest
from aresponses import Response, ResponsesMockServer
from bsblan import BSBLAN, BSBLANConfig
from bsblan.constants import MULTI_PARAMETER_ERROR_MSG
from bsblan.exceptions import (
BSBLANError,
BSBLANInvalidParameterError,
)
if TYPE_CHECKING:
from collections.abc import AsyncGenerator
logger = logging.getLogger(__name__)
@pytest.fixture
async def mock_bsblan() -> AsyncGenerator[BSBLAN, None]:
"""Fixture to create a mocked BSBLAN instance."""
config = BSBLANConfig(host="example.com")
async with aiohttp.ClientSession() as session:
bsblan = BSBLAN(config, session=session)
bsblan._firmware_version = "1.0.38-20200730234859"
bsblan._api_version = "v3"
bsblan._min_temp = 8.0
bsblan._max_temp = 30.0
bsblan._temperature_range_initialized = True
yield bsblan
@pytest.fixture
async def mock_aresponses() -> AsyncGenerator[ResponsesMockServer, None]:
"""Fixture to mock aiohttp responses."""
async with ResponsesMockServer() as server:
# Mock response for /JQ
server.add(
"example.com",
"/JQ",
"POST",
Response(
text=json.dumps(
{"714.0": {"value": "8.0"}, "716.0": {"value": "30.0"}},
),
content_type="application/json",
),
)
yield server
def create_response_handler(expected_data: dict[str, Any]) -> Response:
"""Create a response handler that checks the request data."""
async def response_handler(request: aiohttp.web.Request) -> Response:
"""Check the request data."""
assert request.method == "POST"
assert request.host == "example.com"
assert request.path_qs == "/JS"
actual_data = json.loads(await request.text())
for key, value in expected_data.items():
assert key in actual_data, f"Expected key '{key}' not found in actual data"
if key == "EnumValue":
# Allow both string and integer representations
assert str(actual_data[key]) == str(value)
else:
assert actual_data[key] == value, (
f"Mismatch for key '{key}': expected {value}, "
f"got {actual_data[key]}"
)
return Response(
text=json.dumps({"status": "success"}),
content_type="application/json",
)
return response_handler
@pytest.mark.asyncio
async def test_change_temperature(
mock_bsblan: BSBLAN,
mock_aresponses: ResponsesMockServer,
) -> None:
"""Test changing BSBLAN temperature."""
expected_data = {
"Parameter": "710",
"Value": "20",
"Type": "1",
}
mock_aresponses.add(
"example.com",
"/JS",
"POST",
create_response_handler(expected_data),
)
await mock_bsblan.thermostat(target_temperature="20")
@pytest.mark.asyncio
async def test_change_hvac_mode(
mock_bsblan: BSBLAN,
mock_aresponses: ResponsesMockServer,
) -> None:
"""Test changing BSBLAN HVAC mode."""
expected_data = {
"Parameter": "700",
"EnumValue": 1, # 1 corresponds to "auto" mode
"Type": "1",
}
mock_aresponses.add(
"example.com",
"/JS",
"POST",
create_response_handler(expected_data),
)
await mock_bsblan.thermostat(hvac_mode="auto")
@pytest.mark.asyncio
async def test_invalid_temperature(mock_bsblan: BSBLAN) -> None:
"""Test setting an invalid temperature."""
with pytest.raises(BSBLANInvalidParameterError):
await mock_bsblan.thermostat(target_temperature="35")
@pytest.mark.asyncio
async def test_invalid_hvac_mode(mock_bsblan: BSBLAN) -> None:
"""Test setting an invalid HVAC mode."""
with pytest.raises(BSBLANInvalidParameterError):
await mock_bsblan.thermostat(hvac_mode="invalid_mode")
@pytest.mark.asyncio
async def test_no_parameters(mock_bsblan: BSBLAN) -> None:
"""Test calling thermostat without parameters."""
with pytest.raises(BSBLANError) as exc_info:
await mock_bsblan.thermostat()
assert str(exc_info.value) == MULTI_PARAMETER_ERROR_MSG
|