diff --git a/.github/workflows/tests.yml b/.github/workflows/tests.yml new file mode 100644 index 0000000..81150f9 --- /dev/null +++ b/.github/workflows/tests.yml @@ -0,0 +1,34 @@ +name: Unit Tests + +on: + push: + branches: [ main ] + pull_request: + branches: [ main ] + +permissions: + contents: read + +jobs: + test: + runs-on: ubuntu-latest + strategy: + matrix: + python-version: ["3.11", "3.12", "3.13"] + + steps: + - uses: actions/checkout@v4 + + - name: Set up Python ${{ matrix.python-version }} + uses: actions/setup-python@v5 + with: + python-version: ${{ matrix.python-version }} + + - name: Install dependencies + run: | + python -m pip install --upgrade pip + pip install . + + - name: Run unit tests + run: | + python -m unittest discover -s tests -v diff --git a/ohme/ohme.py b/ohme/ohme.py index 57cd882..79132f5 100644 --- a/ohme/ohme.py +++ b/ohme/ohme.py @@ -299,7 +299,7 @@ def slots_full(self) -> list[ChargeSlot]: @property def solar_enabled(self) -> bool: """Solar mode state.""" - return self._configuration.get("solarMode") == "ZERO_EXPORT" + return self._configuration.get("solarMode") in ("ZERO_EXPORT", "PURE_SOLAR") @property def next_slot_start(self) -> datetime.datetime | None: @@ -610,8 +610,8 @@ async def async_update_device_info(self) -> bool: if resp["tariff"] is not None and resp["tariff"]["dsrTariff"]: self.cap_available = False - solar_modes = device["modelCapabilities"]["solarModes"] - if isinstance(solar_modes, list) and len(solar_modes) == 1: + solar_modes = device["modelCapabilities"].get("solarModes") + if isinstance(solar_modes, list) and len(solar_modes) > 0: self._capabilities["solar"] = True return True diff --git a/tests/test_solar.py b/tests/test_solar.py new file mode 100644 index 0000000..4af3626 --- /dev/null +++ b/tests/test_solar.py @@ -0,0 +1,91 @@ +import unittest +from unittest.mock import AsyncMock, patch +from ohme import OhmeApiClient + + +class TestSolar(unittest.IsolatedAsyncioTestCase): + def test_solar_enabled_property(self): + client = OhmeApiClient("test@example.com", "password") + + # Off / IGNORE + client._configuration = {"solarMode": "IGNORE"} + self.assertFalse(client.solar_enabled) + + # ZERO_EXPORT + client._configuration = {"solarMode": "ZERO_EXPORT"} + self.assertTrue(client.solar_enabled) + + # PURE_SOLAR + client._configuration = {"solarMode": "PURE_SOLAR"} + self.assertTrue(client.solar_enabled) + + # None / Missing + client._configuration = {} + self.assertFalse(client.solar_enabled) + + async def test_solar_capability_multiple_modes(self): + client = OhmeApiClient("test@example.com", "password") + + account_resp = { + "user": {"id": "user123"}, + "tariff": None, + "chargeDevices": [{ + "id": "ohme123", + "modelTypeDisplayName": "Ohme Home Pro", + "firmwareVersionLabel": "v1.0.0", + "modelCapabilities": { + "solarModes": ["PURE_SOLAR", "ZERO_EXPORT"] + }, + "optionalSettings": {} + }] + } + + with patch.object(client, "_make_request", new=AsyncMock(return_value=account_resp)): + self.assertTrue(await client.async_update_device_info()) + self.assertTrue(client.is_capable("solar")) + + async def test_solar_capability_single_mode(self): + client = OhmeApiClient("test@example.com", "password") + + account_resp = { + "user": {"id": "user123"}, + "tariff": None, + "chargeDevices": [{ + "id": "ohme123", + "modelTypeDisplayName": "Ohme Home Pro", + "firmwareVersionLabel": "v1.0.0", + "modelCapabilities": { + "solarModes": ["ZERO_EXPORT"] + }, + "optionalSettings": {} + }] + } + + with patch.object(client, "_make_request", new=AsyncMock(return_value=account_resp)): + self.assertTrue(await client.async_update_device_info()) + self.assertTrue(client.is_capable("solar")) + + async def test_solar_capability_empty_or_missing(self): + client = OhmeApiClient("test@example.com", "password") + + account_resp = { + "user": {"id": "user123"}, + "tariff": None, + "chargeDevices": [{ + "id": "ohme123", + "modelTypeDisplayName": "Ohme Home Pro", + "firmwareVersionLabel": "v1.0.0", + "modelCapabilities": { + "solarModes": [] + }, + "optionalSettings": {} + }] + } + + with patch.object(client, "_make_request", new=AsyncMock(return_value=account_resp)): + self.assertTrue(await client.async_update_device_info()) + self.assertFalse(client.is_capable("solar")) + + +if __name__ == "__main__": + unittest.main()