Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
50 changes: 46 additions & 4 deletions openevsehttp/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,9 +4,11 @@
import asyncio
import datetime
import logging
from json.decoder import JSONDecodeError
from typing import Any, Callable, Optional

import aiohttp # type: ignore
from awesomeversion import AwesomeVersion

from .const import MAX_AMPS, MIN_AMPS
from .exceptions import (
Expand Down Expand Up @@ -200,6 +202,8 @@ async def process_request(
message = await resp.json()
except TimeoutError:
_LOGGER.error("%s: %s", ERROR_TIMEOUT, url)
except JSONDecodeError:
message = {"msg": resp}

if resp.status == 400:
_LOGGER.error("%s", message["msg"])
Expand Down Expand Up @@ -346,6 +350,29 @@ async def set_charge_mode(self, mode: str = "fast") -> None:
_LOGGER.error("Problem issuing command: %s", response["msg"])
raise UnknownError

async def divert_mode(self, mode: str = "Normal") -> None:
"""Set the divert mode to either Normal or Eco modes."""
url = f"{self.url}divertmode"

if mode != "Normal" or mode != "Eco":
_LOGGER.error("Invalid value for divertmode: %s", mode)
raise ValueError

if mode == "Normal":
value = 1
else:
value = 2

data = {"divertmode": value}

_LOGGER.debug("Setting charge mode to %s", mode)
response = await self.process_request(
url=url, method="post", data=data
) # noqa: E501
if response["msg"] != "done":
_LOGGER.error("Problem issuing command: %s", response["msg"])
raise UnknownError

async def get_override(self) -> None:
"""Get the manual override status."""
url = f"{self.url}override"
Expand Down Expand Up @@ -386,11 +413,26 @@ async def set_override(

async def toggle_override(self) -> None:
"""Toggle the manual override status."""
url = f"{self.url}override"
# 3.x: use RAPI commands $FE (enable) and $FS (sleep)
# 4.x: use HTTP API call

_LOGGER.debug("Toggling manual override %s", url)
response = await self.process_request(url=url, method="patch")
_LOGGER.debug("Toggle response: %s", response["msg"])
cutoff = AwesomeVersion("4.0.0")
current = AwesomeVersion(self._config["version"])

_LOGGER.debug("Detected firmware: %s", current)

if cutoff <= current:
url = f"{self.url}override"

_LOGGER.debug("Toggling manual override %s", url)
response = await self.process_request(url=url, method="patch")
_LOGGER.debug("Toggle response: %s", response)
else:
# Older firmware use RAPI commands
_LOGGER.debug("Toggling manual override via RAPI")
command = "$FE" if self._status["state"] == "sleeping" else "$FS"
response = await self.send_command(command)
_LOGGER.debug("Toggle response: %s", response[1])

async def clear_override(self) -> None:
"""Clear the manual override status."""
Expand Down
1 change: 1 addition & 0 deletions requirements.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
awesomeversion
7 changes: 4 additions & 3 deletions requirements_lint.txt
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
black==21.12b0
flake8==4.0.1
mypy==0.931
-r requirements.txt
black==21.7b0
flake8==3.9.2
mypy==0.910
pydocstyle==6.1.1
pylint==2.12.2
1 change: 1 addition & 0 deletions requirements_test.txt
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
-r requirements.txt
pytest==6.2.5
pytest-cov==3.0.0
pytest-timeout==2.0.2
Expand Down
2 changes: 1 addition & 1 deletion setup.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@

PROJECT_DIR = Path(__file__).parent.resolve()
README_FILE = PROJECT_DIR / "README.md"
VERSION = "0.1.13"
VERSION = "0.1.14"


setup(
Expand Down
6 changes: 3 additions & 3 deletions tests/conftest.py
Original file line number Diff line number Diff line change
@@ -1,12 +1,12 @@
"""Provide common pytest fixtures."""
import pytest
import json

import pytest
from aioresponses import aioresponses

import openevsehttp
from tests.common import load_fixture

from aioresponses import aioresponses

TEST_URL_STATUS = "http://openevse.test.tld/status"
TEST_URL_CONFIG = "http://openevse.test.tld/config"
TEST_URL_RAPI = "http://openevse.test.tld/r"
Expand Down
2 changes: 1 addition & 1 deletion tests/fixtures/v4_json/status.json
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@
"vehicle": 1,
"colour": 6,
"freeram": 223464,
"divertmode": 1,
"divertmode": 0,
"srssi": -61,
"elapsed": 246,
"wattsec": 992549,
Expand Down
102 changes: 102 additions & 0 deletions tests/test_init.py
Original file line number Diff line number Diff line change
@@ -1,11 +1,16 @@
import asyncio
import json

import logging

import pytest

import openevsehttp

pytestmark = pytest.mark.asyncio

TEST_URL_RAPI = "http://openevse.test.tld/r"
TEST_URL_OVERRIDE = "http://openevse.test.tld/override"


async def test_get_status_auth(test_charger_auth):
Expand Down Expand Up @@ -508,3 +513,100 @@ async def test_get_relayt(fixture, expected, request):
await charger.update()
status = charger.stuck_relay_check_enabled
assert status == expected


@pytest.mark.parametrize(
"fixture, expected", [("test_charger", "eco"), ("test_charger_v2", "normal")]
)
async def test_get_divertmode(fixture, expected, request):
"""Test v4 Status reply"""
charger = request.getfixturevalue(fixture)
await charger.update()
status = charger.divertmode
assert status == expected


@pytest.mark.parametrize(
"fixture, expected", [("test_charger", 0), ("test_charger_v2", 0)]
)
async def test_get_charge_rate(fixture, expected, request):
"""Test v4 Status reply"""
charger = request.getfixturevalue(fixture)
await charger.update()
status = charger.charge_rate
assert status == expected


@pytest.mark.parametrize(
"fixture, expected", [("test_charger", 0), ("test_charger_v2", 0)]
)
async def test_get_available_current(fixture, expected, request):
"""Test v4 Status reply"""
charger = request.getfixturevalue(fixture)
await charger.update()
with pytest.raises(KeyError):
status = charger.available_current
# assert status == expected


@pytest.mark.parametrize(
"fixture, expected", [("test_charger", 0), ("test_charger_v2", 0)]
)
async def test_get_smoothed_available_current(fixture, expected, request):
"""Test v4 Status reply"""
charger = request.getfixturevalue(fixture)
await charger.update()
with pytest.raises(KeyError):
status = charger.smoothed_available_current
# assert status == expected


@pytest.mark.parametrize(
"fixture, expected", [("test_charger", 0), ("test_charger_v2", 0)]
)
async def test_get_divert_active(fixture, expected, request):
"""Test v4 Status reply"""
charger = request.getfixturevalue(fixture)
await charger.update()
with pytest.raises(KeyError):
status = charger.divert_active
# assert status == expected


@pytest.mark.parametrize(
"fixture, expected", [("test_charger", 0), ("test_charger_v2", 0)]
)
async def test_get_manual_override(fixture, expected, request):
"""Test v4 Status reply"""
charger = request.getfixturevalue(fixture)
await charger.update()
with pytest.raises(KeyError):
status = charger.manual_override
# assert status == expected


async def test_toggle_override(test_charger, mock_aioclient, caplog):
"""Test v4 Status reply"""
await test_charger.update()
mock_aioclient.patch(
TEST_URL_OVERRIDE,
status=200,
body="OK",
)
with caplog.at_level(logging.DEBUG):
await test_charger.toggle_override()
assert "Toggling manual override http" in caplog.text


async def test_toggle_override_v2(test_charger_v2, mock_aioclient, caplog):
"""Test v4 Status reply"""
await test_charger_v2.update()
value = {"cmd": "OK", "ret": "$OK^20"}
mock_aioclient.post(
TEST_URL_RAPI,
status=200,
body=json.dumps(value),
)
with caplog.at_level(logging.DEBUG):
await test_charger_v2.toggle_override()
assert "Toggling manual override via RAPI" in caplog.text