Use Py-ccm15

This commit is contained in:
Oscar Calvo
2023-08-11 02:11:35 +00:00
parent b3154266ee
commit c18b789cc3
5 changed files with 13 additions and 137 deletions

View File

@@ -1,12 +1,10 @@
"""Climate device for CCM15 coordinator."""
import asyncio
import datetime
import logging
from typing import Any, Optional
import aiohttp
from ccm15 import CCM15Device, CCM15DeviceState, CCM15SlaveDevice
import httpx
import xmltodict
from homeassistant.components.climate import (
FAN_AUTO,
@@ -19,10 +17,7 @@ from homeassistant.components.climate import (
ClimateEntityFeature,
HVACMode,
)
from homeassistant.const import (
ATTR_TEMPERATURE,
UnitOfTemperature,
)
from homeassistant.const import ATTR_TEMPERATURE, UnitOfTemperature
from homeassistant.core import HomeAssistant
from homeassistant.helpers.entity import DeviceInfo
from homeassistant.helpers.update_coordinator import (
@@ -32,9 +27,6 @@ from homeassistant.helpers.update_coordinator import (
)
from .const import (
BASE_URL,
CONF_URL_CTRL,
CONF_URL_STATUS,
CONST_CMD_FAN_MAP,
CONST_CMD_STATE_MAP,
CONST_FAN_CMD_MAP,
@@ -42,7 +34,6 @@ from .const import (
DEFAULT_TIMEOUT,
DOMAIN,
)
from .data_model import CCM15DeviceState, CCM15SlaveDevice
_LOGGER = logging.getLogger(__name__)
@@ -61,8 +52,8 @@ class CCM15Coordinator(DataUpdateCoordinator[CCM15DeviceState]):
update_method=self._async_update_data,
update_interval=datetime.timedelta(seconds=interval),
)
self._ccm15 = CCM15Device(host, port, DEFAULT_TIMEOUT)
self._host = host
self._port = port
self._ac_devices: dict[int, CCM15Climate] = {}
def get_devices(self):
@@ -76,31 +67,9 @@ class CCM15Coordinator(DataUpdateCoordinator[CCM15DeviceState]):
except httpx.RequestError as err: # pragma: no cover
raise UpdateFailed(f"Error communicating with Device: {err}") from err
async def _fetch_xml_data(self) -> str: # pragma: no cover
url = BASE_URL.format(self._host, self._port, CONF_URL_STATUS)
_LOGGER.debug("Querying url:'%s'", url)
async with httpx.AsyncClient() as client:
response = await client.get(url, timeout=DEFAULT_TIMEOUT)
return response.text
async def _fetch_data(self) -> CCM15DeviceState:
"""Get the current status of all AC devices."""
str_data = await self._fetch_xml_data()
doc = xmltodict.parse(str_data)
data = doc["response"]
_LOGGER.debug("Found %s items in host %s", len(data.items()), self._host)
ac_data = CCM15DeviceState(devices={})
ac_index = 0
for ac_name, ac_binary in data.items():
_LOGGER.debug("Found ac_name:'%s', data:'%s'", ac_name, ac_binary)
if ac_binary == "-":
break
bytesarr = bytes.fromhex(ac_binary.strip(","))
ac_slave = CCM15SlaveDevice(bytesarr)
_LOGGER.debug("Index: %s, state:'%s'", ac_index, ac_slave)
ac_data.devices[ac_index] = ac_slave
ac_index += 1
_LOGGER.debug("Found data '%s'", ac_data.devices)
ac_data = await self._ccm15.get_status_async()
if len(self._ac_devices) == 0:
for ac_index in ac_data.devices:
_LOGGER.debug("Creating new ac device at index '%s'", ac_index)
@@ -109,45 +78,11 @@ class CCM15Coordinator(DataUpdateCoordinator[CCM15DeviceState]):
async def async_test_connection(self): # pragma: no cover
"""Test the connection to the CCM15 device."""
url = f"http://{self._host}:{self._port}/{CONF_URL_STATUS}"
try:
async with aiohttp.ClientSession() as session, session.get(
url, timeout=10
) as response:
if response.status == 200:
return True
_LOGGER.debug("Test connection: Cannot connect : %s", response.status)
return False
except (aiohttp.ClientError, asyncio.TimeoutError):
_LOGGER.debug("Test connection: Timeout")
return False
async def async_send_state(self, url: str) -> bool: # pragma: no cover
"""Send the url to set state to the ccm15 slave."""
async with httpx.AsyncClient() as client:
response = await client.get(url, timeout=DEFAULT_TIMEOUT)
_LOGGER.debug("API response status code:%d", response.status_code)
return response.status_code in (httpx.codes.OK, httpx.codes.FOUND)
return await self._ccm15.async_test_connection()
async def async_set_state(self, ac_index: int, state: str, value: int) -> None:
"""Set new target states."""
_LOGGER.debug("Calling async_set_states for ac index '%s'", ac_index)
ac_id: int = 2**ac_index
url = BASE_URL.format(
self._host,
self._port,
CONF_URL_CTRL
+ "?ac0="
+ str(ac_id)
+ "&ac1=0"
+ "&"
+ state
+ "="
+ str(value),
)
_LOGGER.debug("Url:'%s'", url)
if await self.async_send_state(url):
if await self._ccm15.async_set_state(ac_index, state, value):
await self.async_request_refresh()
def get_ac_data(self, ac_index: int) -> Optional[CCM15SlaveDevice]:

View File

@@ -1,63 +0,0 @@
"""Data model to represent state of a CCM15 device."""
from dataclasses import dataclass
import logging
from homeassistant.const import (
UnitOfTemperature,
)
_LOGGER = logging.getLogger(__name__)
@dataclass
class CCM15SlaveDevice:
"""Data retrieved from a CCM15 slave device."""
def __init__(self, bytesarr: bytes) -> None:
"""Initialize the slave device."""
self.unit = UnitOfTemperature.CELSIUS
buf = bytesarr[0]
if (buf >> 0) & 1:
self.unit = UnitOfTemperature.FAHRENHEIT
self.locked_cool_temperature: int = (buf >> 3) & 0x1F
buf = bytesarr[1]
self.locked_heat_temperature: int = (buf >> 0) & 0x1F
self.locked_wind: int = (buf >> 5) & 7
buf = bytesarr[2]
self.locked_ac_mode: int = (buf >> 0) & 3
self.error_code: int = (buf >> 2) & 0x3F
buf = bytesarr[3]
self.ac_mode: int = (buf >> 2) & 7
self.fan_mode: int = (buf >> 5) & 7
buf = (buf >> 1) & 1
self.is_ac_mode_locked: bool = buf != 0
buf = bytesarr[4]
self.temperature_setpoint: int = (buf >> 3) & 0x1F
if self.unit == UnitOfTemperature.FAHRENHEIT:
self.temperature_setpoint += 62
self.locked_cool_temperature += 62
self.locked_heat_temperature += 62
self.is_swing_on: bool = (buf >> 1) & 1 != 0
buf = bytesarr[5]
if ((buf >> 3) & 1) == 0:
self.locked_cool_temperature = 0
if ((buf >> 4) & 1) == 0:
self.locked_heat_temperature = 0
self.fan_locked: bool = buf >> 5 & 1 != 0
self.is_remote_locked: bool = ((buf >> 6) & 1) != 0
buf = bytesarr[6]
self.temperature: int = buf if buf < 128 else buf - 256
@dataclass
class CCM15DeviceState:
"""Data retrieved from a CCM15 device."""
devices: dict[int, CCM15SlaveDevice]

View File

@@ -7,7 +7,7 @@
"documentation": "https://www.home-assistant.io/integrations/ccm15",
"homekit": {},
"iot_class": "local_polling",
"requirements": ["xmltodict==0.13.0"],
"requirements": ["py-ccm15==0.0.5"],
"ssdp": [],
"zeroconf": []
}

View File

@@ -1483,6 +1483,9 @@ pvo==1.0.0
# homeassistant.components.canary
py-canary==0.5.3
# homeassistant.components.ccm15
py-ccm15==0.0.5
# homeassistant.components.cpuspeed
py-cpuinfo==8.0.0
@@ -2707,7 +2710,6 @@ xknx==2.11.2
xknxproject==3.2.0
# homeassistant.components.bluesound
# homeassistant.components.ccm15
# homeassistant.components.fritz
# homeassistant.components.rest
# homeassistant.components.startca

View File

@@ -1116,6 +1116,9 @@ pvo==1.0.0
# homeassistant.components.canary
py-canary==0.5.3
# homeassistant.components.ccm15
py-ccm15==0.0.5
# homeassistant.components.cpuspeed
py-cpuinfo==8.0.0
@@ -1992,7 +1995,6 @@ xknx==2.11.2
xknxproject==3.2.0
# homeassistant.components.bluesound
# homeassistant.components.ccm15
# homeassistant.components.fritz
# homeassistant.components.rest
# homeassistant.components.startca