diff --git a/custom_components/salusfy/climate.py b/custom_components/salusfy/climate.py
index 80aa095..ee8fb87 100644
--- a/custom_components/salusfy/climate.py
+++ b/custom_components/salusfy/climate.py
@@ -5,8 +5,8 @@
import time
import logging
import re
-import requests
-import json
+import json
+import aiohttp
import homeassistant.helpers.config_validation as cv
import voluptuous as vol
@@ -34,12 +34,11 @@
PLATFORM_SCHEMA,
)
-
from homeassistant.helpers.reload import async_setup_reload_service
+from asyncio import Lock
__version__ = "0.0.3"
-
_LOGGER = logging.getLogger(__name__)
URL_LOGIN = "https://salus-it500.com/public/login.php"
@@ -49,20 +48,19 @@
DEFAULT_NAME = "Salus Thermostat"
-
CONF_NAME = "name"
# Values from web interface
MIN_TEMP = 5
MAX_TEMP = 34.5
-SUPPORT_FLAGS = ClimateEntityFeature.TARGET_TEMPERATURE
+SUPPORT_FLAGS = (
+ ClimateEntityFeature.TARGET_TEMPERATURE | ClimateEntityFeature.TURN_ON | ClimateEntityFeature.TURN_OFF
+)
DOMAIN = "salusfy"
PLATFORMS = ["climate"]
-
-
PLATFORM_SCHEMA = PLATFORM_SCHEMA.extend(
{
vol.Optional(CONF_NAME, default=DEFAULT_NAME): cv.string,
@@ -72,22 +70,14 @@
}
)
-
-# def setup_platform(hass, config, add_entities, discovery_info=None):
async def async_setup_platform(hass, config, async_add_entities, discovery_info=None):
await async_setup_reload_service(hass, DOMAIN, PLATFORMS)
- """Set up the E-Thermostaat platform."""
name = config.get(CONF_NAME)
username = config.get(CONF_USERNAME)
password = config.get(CONF_PASSWORD)
id = config.get(CONF_ID)
- # add_entities(
- # [SalusThermostat(name, username, password, id)]
- async_add_entities(
- [SalusThermostat(name, username, password, id)]
- )
-
+ async_add_entities([SalusThermostat(name, username, password, id)])
class SalusThermostat(ClimateEntity):
"""Representation of a Salus Thermostat device."""
@@ -104,12 +94,13 @@ def __init__(self, name, username, password, id):
self._status = None
self._current_operation_mode = None
self._token = None
-
- self._session = requests.Session()
-
-
- self.update()
-
+ self._session = aiohttp.ClientSession()
+ self._update_lock = Lock()
+
+ async def close(self):
+ """Close the aiohttp session."""
+ await self._session.close()
+
@property
def supported_features(self):
"""Return the list of supported features."""
@@ -119,7 +110,7 @@ def supported_features(self):
def name(self):
"""Return the name of the thermostat."""
return self._name
-
+
@property
def unique_id(self) -> str:
"""Return the unique ID for this thermostat."""
@@ -155,21 +146,12 @@ def target_temperature(self):
"""Return the temperature we try to reach."""
return self._target_temperature
-
@property
def hvac_mode(self):
"""Return hvac operation ie. heat, cool mode."""
- try:
- climate_mode = self._current_operation_mode
- curr_hvac_mode = HVACMode.OFF
- if climate_mode == "ON":
- curr_hvac_mode = HVACMode.HEAT
- else:
- curr_hvac_mode = HVACMode.OFF
- except KeyError:
- return HVACMode.OFF
- return curr_hvac_mode
-
+ climate_mode = self._current_operation_mode
+ return HVACMode.HEAT if climate_mode == "ON" else HVACMode.OFF
+
@property
def hvac_modes(self):
"""HVAC modes."""
@@ -178,109 +160,115 @@ def hvac_modes(self):
@property
def hvac_action(self):
"""Return the current running hvac operation."""
- if self._status == "ON":
- return HVACAction.HEATING
- return HVACAction.IDLE
-
+ return HVACAction.HEATING if self._status == "ON" else HVACAction.IDLE
@property
def preset_mode(self):
"""Return the current preset mode, e.g., home, away, temp."""
return self._status
-
+
@property
def preset_modes(self):
"""Return a list of available preset modes."""
- return SUPPORT_PRESET
-
-
- def set_temperature(self, **kwargs):
+ return [] # Define preset modes if applicable
+
+ async def set_temperature(self, **kwargs):
"""Set new target temperature."""
temperature = kwargs.get(ATTR_TEMPERATURE)
- if temperature is None:
- return
- self._set_temperature(temperature)
+ if temperature is not None:
+ await self._set_temperature(temperature)
- def _set_temperature(self, temperature):
+ async def _set_temperature(self, temperature):
"""Set new target temperature, via URL commands."""
- payload = {"token": self._token, "devId": self._id, "tempUnit": "0", "current_tempZ1_set": "1", "current_tempZ1": temperature}
+ payload = {
+ "token": self._token,
+ "devId": self._id,
+ "tempUnit": "0",
+ "current_tempZ1_set": "1",
+ "current_tempZ1": temperature,
+ }
headers = {"content-type": "application/x-www-form-urlencoded"}
- try:
- if self._session.post(URL_SET_DATA, data=payload, headers=headers):
+ async with self._session.post(URL_SET_DATA, data=payload, headers=headers) as response:
+ if response.status == 200:
self._target_temperature = temperature
- # self.schedule_update_ha_state(force_refresh=True)
- _LOGGER.info("Salusfy set_temperature OK")
- except:
- _LOGGER.error("Error Setting the temperature.")
+ _LOGGER.info("Salusfy set_temperature OK")
+ else:
+ _LOGGER.error(f"Failed to set temperature, status: {response.status}")
- def set_hvac_mode(self, hvac_mode):
+ async def set_hvac_mode(self, hvac_mode):
"""Set HVAC mode, via URL commands."""
-
headers = {"content-type": "application/x-www-form-urlencoded"}
- if hvac_mode == HVACMode.OFF:
- payload = {"token": self._token, "devId": self._id, "auto": "1", "auto_setZ1": "1"}
- try:
- if self._session.post(URL_SET_DATA, data=payload, headers=headers):
- self._current_operation_mode = "OFF"
- except:
- _LOGGER.error("Error Setting HVAC mode OFF.")
- elif hvac_mode == HVACMode.HEAT:
- payload = {"token": self._token, "devId": self._id, "auto": "0", "auto_setZ1": "1"}
- try:
- if self._session.post(URL_SET_DATA, data=payload, headers=headers):
- self._current_operation_mode = "ON"
- except:
- _LOGGER.error("Error Setting HVAC mode.")
- _LOGGER.info("Setting the HVAC mode.")
-
- def get_token(self):
+ payload = {
+ "token": self._token,
+ "devId": self._id,
+ "auto": "1" if hvac_mode == HVACMode.OFF else "0",
+ "auto_setZ1": "1",
+ }
+ async with self._session.post(URL_SET_DATA, data=payload, headers=headers) as response:
+ if response.status == 200:
+ self._current_operation_mode = "OFF" if hvac_mode == HVACMode.OFF else "ON"
+ _LOGGER.info("Salusfy set_hvac_mode OK")
+ else:
+ _LOGGER.error(f"Failed to set HVAC mode, status: {response.status}")
+
+ async def get_token(self):
"""Get the Session Token of the Thermostat."""
- payload = {"IDemail": self._username, "password": self._password, "login": "Login", "keep_logged_in": "1"}
+ payload = {
+ "IDemail": self._username,
+ "password": self._password,
+ "login": "Login",
+ "keep_logged_in": "1",
+ }
headers = {"content-type": "application/x-www-form-urlencoded"}
-
- try:
- self._session.post(URL_LOGIN, data=payload, headers=headers)
- params = {"devId": self._id}
- getTkoken = self._session.get(URL_GET_TOKEN,params=params)
- result = re.search('', getTkoken.text)
- _LOGGER.info("Salusfy get_token OK")
- self._token = result.group(1)
- except:
- _LOGGER.error("Error Geting the Session Token.")
-
- def _get_data(self):
+ async with self._session.post(URL_LOGIN, data=payload, headers=headers) as response:
+ if response.status == 200:
+ params = {"devId": self._id}
+ async with self._session.get(URL_GET_TOKEN, params=params) as token_response:
+ text = await token_response.text()
+ result = re.search('', text)
+ if result:
+ self._token = result.group(1)
+ _LOGGER.info("Salusfy get_token OK")
+ else:
+ _LOGGER.error("Failed to extract token.")
+ else:
+ _LOGGER.error(f"Login failed with status {response.status}.")
+
+ async def _get_data(self):
if self._token is None:
- self.get_token()
- params = {"devId": self._id, "token": self._token, "&_": str(int(round(time.time() * 1000)))}
+ await self.get_token()
+ params = {
+ "devId": self._id,
+ "token": self._token,
+ "&_": str(int(round(time.time() * 1000))),
+ }
try:
- r = self._session.get(url = URL_GET_DATA, params = params)
- try:
- if r:
- data = json.loads(r.text)
- _LOGGER.info("Salusfy get_data output OK")
- self._target_temperature = float(data["CH1currentSetPoint"])
- self._current_temperature = float(data["CH1currentRoomTemp"])
- self._frost = float(data["frost"])
-
- status = data['CH1heatOnOffStatus']
- if status == "1":
- self._status = "ON"
+ async with self._session.get(URL_GET_DATA, params=params) as response:
+ if response.status == 200:
+ content_type = response.headers.get('Content-Type', '')
+ text = await response.text()
+ if 'application/json' in content_type or text.startswith('{'):
+ data = json.loads(text)
+ _LOGGER.info("Salusfy get_data output OK")
+ self._target_temperature = float(data["CH1currentSetPoint"])
+ self._current_temperature = float(data["CH1currentRoomTemp"])
+ self._frost = float(data["frost"])
+ self._status = "ON" if data['CH1heatOnOffStatus'] == "1" else "OFF"
+ self._current_operation_mode = "OFF" if data['CH1heatOnOff'] == "1" else "ON"
else:
- self._status = "OFF"
- mode = data['CH1heatOnOff']
- if mode == "1":
- self._current_operation_mode = "OFF"
- else:
- self._current_operation_mode = "ON"
+ _LOGGER.error(f"Unexpected content type: {content_type}. Response: {text}")
+ elif response.status == 401:
+ _LOGGER.warning("Token expired, re-authenticating.")
+ await self.get_token()
+ await self._get_data()
else:
- _LOGGER.error("Could not get data from Salus.")
- except:
- self.get_token()
- self._get_data()
- except:
- _LOGGER.error("Error Geting the data from Web. Please check the connection to salus-it500.com manually.")
-
- def update(self):
- """Get the latest data."""
- self._get_data()
-
+ _LOGGER.error(f"Failed to get data, status: {response.status}")
+ except aiohttp.ClientError as e:
+ _LOGGER.error(f"Network error occurred: {e}")
+ except json.JSONDecodeError:
+ _LOGGER.error("Failed to decode JSON response.")
+
+ async def async_update(self):
+ """Asynchronous update for Home Assistant."""
+ async with self._update_lock:
+ await self._get_data()