Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Switch to async #10

Merged
merged 2 commits into from
Feb 25, 2024
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
3 changes: 2 additions & 1 deletion requirements.txt
Original file line number Diff line number Diff line change
@@ -1 +1,2 @@
requests
requests
requests_async
8 changes: 4 additions & 4 deletions run.py
Original file line number Diff line number Diff line change
Expand Up @@ -26,11 +26,11 @@ async def main():

thermostat = registry.first

thermostat.update()
thermostat.update()
await thermostat.async_update()
await thermostat.async_update()

thermostat.set_hvac_mode(HVACMode.Heat)
thermostat.set_temperature(temperature=9.8)
await thermostat.set_hvac_mode(HVACMode.Heat)
await thermostat.set_temperature(temperature=9.8)

print("Current: " + str(thermostat.current_temperature))
print("Target: " + str(thermostat.target_temperature))
Expand Down
16 changes: 7 additions & 9 deletions salusfy/client.py
Original file line number Diff line number Diff line change
Expand Up @@ -18,33 +18,31 @@ def __init__(self, web_client : WebClient, temperature_client : HaTemperatureCli
self._web_client = web_client
self._temperature_client = temperature_client

self.get_state()


def set_temperature(self, temperature):
async def set_temperature(self, temperature):
"""Set new target temperature."""

_LOGGER.info("Delegating set_temperature to web client...")

self._web_client.set_temperature(temperature)
await self._web_client.set_temperature(temperature)


def set_hvac_mode(self, hvac_mode):
async def set_hvac_mode(self, hvac_mode):
"""Set HVAC mode, via URL commands."""

_LOGGER.info("Delegating set_hvac_mode to web client...")

self._web_client.set_hvac_mode(hvac_mode)
await self._web_client.set_hvac_mode(hvac_mode)


def get_state(self):
async def get_state(self):
"""Retrieves the status"""

if self._state is None:
_LOGGER.info("Delegating get_state to web client...")
self._state = self._web_client.get_state()
self._state = await self._web_client.get_state()

_LOGGER.info("Updating current temperature from temperature client...")
self._state.current_temperature = self._temperature_client.current_temperature()
self._state.current_temperature = await self._temperature_client.current_temperature()

return self._state
6 changes: 3 additions & 3 deletions salusfy/ha_temperature_client.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
from requests import get
from requests_async import get

"""
Retrieves the current temperature from
Expand All @@ -12,7 +12,7 @@ def __init__(self, host, entity_id, access_token):
self._access_token = access_token


def current_temperature(self):
async def current_temperature(self):
"""Gets the current temperature from HA"""

url = F"http://{self._host}:8123/api/states/{self._entity_id}"
Expand All @@ -22,7 +22,7 @@ def current_temperature(self):
"Content-Type": "application/json",
}

response = get(url, headers=headers)
response = await get(url, headers=headers)

body = response.json()

Expand Down
2 changes: 1 addition & 1 deletion salusfy/simulator/temperature_client.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,5 +5,5 @@ class TemperatureClient:
def __init__(self):
pass

def current_temperature(self):
async def current_temperature(self):
return 15.9
6 changes: 3 additions & 3 deletions salusfy/simulator/web_client.py
Original file line number Diff line number Diff line change
Expand Up @@ -28,15 +28,15 @@ def __init__(self):
self._state.frost = 10.1


def set_temperature(self, temperature):
async def set_temperature(self, temperature):
"""Set new target temperature."""

_LOGGER.info("Setting temperature to %.1f...", temperature)

self._state.target_temperature = temperature


def set_hvac_mode(self, hvac_mode):
async def set_hvac_mode(self, hvac_mode):
"""Set HVAC mode, via URL commands."""

_LOGGER.info("Setting the HVAC mode to %s...", hvac_mode)
Expand All @@ -47,6 +47,6 @@ def set_hvac_mode(self, hvac_mode):
self._state.current_operation_mode = STATE_ON


def get_state(self):
async def get_state(self):
"""Retrieves the mock status"""
return self._state
17 changes: 8 additions & 9 deletions salusfy/thermostat_entity.py
Original file line number Diff line number Diff line change
Expand Up @@ -34,8 +34,7 @@ def __init__(self, name, client):
self._name = name
self._client = client
self._state = None

self.update()


@property
def supported_features(self):
Expand Down Expand Up @@ -121,23 +120,23 @@ def preset_modes(self):
return SUPPORT_PRESET_MODE


def set_temperature(self, **kwargs):
async def set_temperature(self, **kwargs):
"""Set new target temperature."""

temperature = kwargs.get(ATTR_TEMPERATURE)

if temperature is None:
return

self._client.set_temperature(temperature)
await self._client.set_temperature(temperature)

self._state.target_temperature = temperature


def set_hvac_mode(self, hvac_mode):
async def set_hvac_mode(self, hvac_mode):
"""Set HVAC mode, via URL commands."""

self._client.set_hvac_mode(hvac_mode)
await self._client.set_hvac_mode(hvac_mode)

if hvac_mode == HVACMode.OFF:
self._state.current_operation_mode = STATE_OFF
Expand All @@ -147,6 +146,6 @@ def set_hvac_mode(self, hvac_mode):
self._state.status = STATE_ON


def update(self):
"""Get the latest state data."""
self._state = self._client.get_state()
async def async_update(self):
"""Retrieve latest state data."""
self._state = await self._client.get_state()
33 changes: 17 additions & 16 deletions salusfy/web_client.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,8 +4,8 @@
import time
import logging
import re
import requests
import json
import requests_async as requests
import json

from .state import State

Expand Down Expand Up @@ -41,12 +41,12 @@ def __init__(self, username, password, id):
self._session = requests.Session()


def set_temperature(self, temperature):
async def set_temperature(self, temperature):
"""Set new target temperature, via URL commands."""

_LOGGER.info("Setting the temperature to %.1f...", temperature)

token = self.obtain_token()
token = await self.obtain_token()

payload = {"token": token, "devId": self._id, "tempUnit": "0", "current_tempZ1_set": "1", "current_tempZ1": temperature}
headers = {"Content-Type": "application/x-www-form-urlencoded"}
Expand All @@ -58,7 +58,7 @@ def set_temperature(self, temperature):
_LOGGER.error("Error Setting the temperature.")


def set_hvac_mode(self, hvac_mode):
async def set_hvac_mode(self, hvac_mode):
"""Set HVAC mode, via URL commands."""

_LOGGER.info("Setting the HVAC mode to %s...", hvac_mode)
Expand All @@ -71,7 +71,7 @@ def set_hvac_mode(self, hvac_mode):
elif hvac_mode == HVAC_MODE_HEAT:
auto = "0"

token = self.obtain_token()
token = await self.obtain_token()

payload = {"token": token, "devId": self._id, "auto": auto, "auto_setZ1": "1"}
try:
Expand All @@ -80,24 +80,24 @@ def set_hvac_mode(self, hvac_mode):
_LOGGER.error("Error Setting HVAC mode to %s", hvac_mode)


def obtain_token(self):
async def obtain_token(self):
"""Gets the existing session token of the thermostat or retrieves a new one if expired."""

if self._token is None:
_LOGGER.info("Retrieving token for the first time this session...")
self.get_token()
await self.get_token()
return self._token

if self._tokenRetrievedAt > time.time() - MAX_TOKEN_AGE_SECONDS:
_LOGGER.info("Using cached token...")
return self._token

_LOGGER.info("Token has expired, getting new one...")
self.get_token()
await self.get_token()
return self._token


def get_token(self):
async def get_token(self):
"""Get the Session Token of the Thermostat."""

_LOGGER.info("Getting token from Salus...")
Expand All @@ -106,29 +106,30 @@ def get_token(self):
headers = {"Content-Type": "application/x-www-form-urlencoded"}

try:
self._session.post(URL_LOGIN, data=payload, headers=headers)
await self._session.post(URL_LOGIN, data=payload, headers=headers, verify=False)
params = {"devId": self._id}
getTkoken = self._session.get(URL_GET_TOKEN, params=params)
getTkoken = await self._session.get(URL_GET_TOKEN, params=params)
result = re.search('<input id="token" type="hidden" value="(.*)" />', getTkoken.text)
_LOGGER.info("Salusfy get_token OK")
self._token = result.group(1)
self._tokenRetrievedAt = time.time()
except:
except Exception as e:
self._token = None
self._tokenRetrievedAt = None
_LOGGER.error("Error getting the session token.")
_LOGGER.error(e)


def get_state(self):
async def get_state(self):
"""Retrieve the current state from the Salus gateway"""

_LOGGER.info("Retrieving current state from Salus Gateway...")

token = self.obtain_token()
token = await self.obtain_token()

params = {"devId": self._id, "token": token, "&_": str(int(round(time.time() * 1000)))}
try:
r = self._session.get(url = URL_GET_DATA, params = params)
r = await self._session.get(url = URL_GET_DATA, params = params)
if not r:
_LOGGER.error("Could not get data from Salus.")
return None
Expand Down
5 changes: 0 additions & 5 deletions test.ps1

This file was deleted.

61 changes: 32 additions & 29 deletions tests/test_client.py
Original file line number Diff line number Diff line change
@@ -1,73 +1,76 @@
import pytest
from unittest.mock import MagicMock
from unittest.mock import Mock

from salusfy import ( Client, State )
from salusfy import ( Client, State, WebClient, HaTemperatureClient )
from homeassistant.components.climate.const import (
HVAC_MODE_HEAT
HVACMode
)

@pytest.fixture
def mock_client():
mock = MagicMock()

state = State()
state.current_temperature = 15.3
state.target_temperature = 33.3
mock.configure_mock(
**{
"get_state.return_value": state
}
)

mock = Mock(WebClient)
mock.get_state.return_value = state

return mock


@pytest.fixture
def mock_ha_client():
mock = MagicMock()
mock = Mock(HaTemperatureClient)

mock.configure_mock(
**{
"current_temperature.return_value": 21.1
}
)
mock.current_temperature.return_value = 21.1

return mock


def test_entity_returns_target_temp_from_web_client(mock_client, mock_ha_client):
@pytest.mark.asyncio
async def test_entity_returns_target_temp_from_web_client(mock_client, mock_ha_client):
target = Client(mock_client, mock_ha_client)

assert target.get_state().target_temperature == 33.3
actual = await target.get_state()

assert actual.target_temperature == 33.3


def test_entity_returns_target_temp_from_home_assistant_client(mock_client, mock_ha_client):
@pytest.mark.asyncio
async def test_entity_returns_target_temp_from_home_assistant_client(mock_client, mock_ha_client):
target = Client(mock_client, mock_ha_client)

assert target.get_state().current_temperature == 21.1
actual = await target.get_state()

assert actual.current_temperature == 21.1


def test_entity_call_salus_client_only_once(mock_client, mock_ha_client):
@pytest.mark.asyncio
async def test_entity_call_salus_client_only_once(mock_client, mock_ha_client):
target = Client(mock_client, mock_ha_client)

target.get_state()
target.get_state()
await target.get_state()
await target.get_state()

mock_client.get_state.assert_called_once()
assert target.get_state().target_temperature == 33.3

actual = await target.get_state()
assert actual.target_temperature == 33.3


def test_entity_delegates_set_temperature_salus_client(mock_client, mock_ha_client):
@pytest.mark.asyncio
async def test_entity_delegates_set_temperature_salus_client(mock_client, mock_ha_client):
target = Client(mock_client, mock_ha_client)

target.set_temperature(temperature=29.9)
await target.set_temperature(temperature=29.9)

mock_client.set_temperature.assert_called_once_with(29.9)


def test_entity_delegates_set_hvac_mode_to_salus_client(mock_client, mock_ha_client):
@pytest.mark.asyncio
async def test_entity_delegates_set_hvac_mode_to_salus_client(mock_client, mock_ha_client):
target = Client(mock_client, mock_ha_client)

target.set_hvac_mode(hvac_mode=HVAC_MODE_HEAT)
await target.set_hvac_mode(hvac_mode=HVACMode.HEAT)

mock_client.set_hvac_mode.assert_called_once_with(HVAC_MODE_HEAT)
mock_client.set_hvac_mode.assert_called_once_with(HVACMode.HEAT)
Loading