File: test_thermostat.py

package info (click to toggle)
python-bsblan 2.2.5-1
  • links: PTS, VCS
  • area: main
  • in suites: forky, sid, trixie
  • size: 784 kB
  • sloc: python: 2,890; makefile: 3
file content (150 lines) | stat: -rw-r--r-- 4,490 bytes parent folder | download
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