Skip to content
Open
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
30 changes: 21 additions & 9 deletions custom_components/cozytouch/capability.py
Original file line number Diff line number Diff line change
Expand Up @@ -68,6 +68,10 @@ def get_capability_infos(modelInfos: dict, capabilityId: int, capabilityValue: s
capability.pop("lowestValueCapabilityId")
capability.pop("highestValueCapabilityId")
capability["icon"] = "mdi:heat-pump"
elif modelInfos["type"] == CozytouchDeviceType.THERMOSTAT:
capability["name"] = "heat"
capability["icon"] = "mdi:thermostat"
capability["targetCapabilityId"] = 40
else:
capability["name"] = "heat"

Expand Down Expand Up @@ -369,9 +373,8 @@ def get_capability_infos(modelInfos: dict, capabilityId: int, capabilityValue: s
capability["highestValueCapabilityId"] = 161

elif capabilityId == 177:
if modelInfos["type"] == CozytouchDeviceType.GAZ_BOILER:
if modelInfos["type"] in [CozytouchDeviceType.GAZ_BOILER, CozytouchDeviceType.THERMOSTAT]:
return {}

capability["name"] = "target_cool_temperature"
capability["type"] = "temperature_adjustment_number"
capability["category"] = "sensor"
Expand Down Expand Up @@ -623,21 +626,24 @@ def get_capability_infos(modelInfos: dict, capabilityId: int, capabilityValue: s
capability["icon"] = "mdi:fire"

elif capabilityId == 100505:
if modelInfos["type"] == CozytouchDeviceType.THERMOSTAT:
return {}
capability["name"] = "powerful_mode"
capability["type"] = "switch"
capability["category"] = "sensor"
capability["icon"] = "mdi:wind-power"

elif capabilityId == 100506:
if modelInfos["type"] == CozytouchDeviceType.TOWEL_RACK:
capability = {}
else:
capability["name"] = "presence_mode"
capability["type"] = "switch"
capability["category"] = "sensor"
capability["icon"] = "mdi:account"
if modelInfos["type"] in [CozytouchDeviceType.TOWEL_RACK, CozytouchDeviceType.THERMOSTAT]:
return {}
capability["name"] = "presence_mode"
capability["type"] = "switch"
capability["category"] = "sensor"
capability["icon"] = "mdi:account"

elif capabilityId == 100507:
if modelInfos["type"] == CozytouchDeviceType.THERMOSTAT:
return {}
capability["name"] = "eco_mode"
capability["type"] = "switch"
capability["category"] = "sensor"
Expand Down Expand Up @@ -714,18 +720,24 @@ def get_capability_infos(modelInfos: dict, capabilityId: int, capabilityValue: s
capability["category"] = "diag"

elif capabilityId == 100802:
if modelInfos["type"] == CozytouchDeviceType.THERMOSTAT:
return {}
capability["name"] = "quiet_mode"
capability["type"] = "switch"
capability["category"] = "sensor"
capability["icon"] = "mdi:fan-minus"

elif capabilityId == 100804:
if modelInfos["type"] == CozytouchDeviceType.THERMOSTAT:
return {}
capability["name"] = "swing_mode"
capability["type"] = "switch"
capability["category"] = "sensor"
capability["icon"] = "mdi:arrow-oscillating"

elif capabilityId == 104044:
if modelInfos["type"] == CozytouchDeviceType.THERMOSTAT:
return {}
capability["name"] = "boost_mode"
capability["type"] = "switch"
capability["category"] = "sensor"
Expand Down
12 changes: 7 additions & 5 deletions custom_components/cozytouch/hub.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
from datetime import UTC, datetime, time as t, timedelta, timezone
import json
import logging
from typing import Any, Dict

from aiohttp import ClientSession, ContentTypeError, FormData

Expand Down Expand Up @@ -226,7 +227,7 @@ def update_devices_from_json_data(self, json_data) -> None:
"modelId": remote_device["modelId"],
"productId": remote_device["productId"],
"zoneId": remote_device["zoneId"],
"modelInfos": get_model_infos(remote_device["modelId"]),
"modelInfos": get_model_infos(remote_device["modelId"], remote_device["tags"]),
"capabilities": [],
"tags": [],
}
Expand Down Expand Up @@ -328,14 +329,15 @@ def get_zone_name(self, zoneId: int | None = None) -> str:

return str(zoneId)

def get_model_infos(self, deviceId: int | None = None) -> str:
def get_model_infos(self, deviceId: int | None = None) -> dict:
"""Get model infos."""
if not deviceId:
deviceId = self._deviceId

for dev in self._devices:
if dev["deviceId"] == deviceId:
zoneId = dev["zoneId"]
tags = dev["tags"]

# Special case for sub-devices, use master zone Id
for masterDev in self._devices:
Expand All @@ -350,9 +352,9 @@ def get_model_infos(self, deviceId: int | None = None) -> str:
zoneId = masterDev["zoneId"]
break

return get_model_infos(dev["modelId"], self.get_zone_name(zoneId))
return get_model_infos(dev["modelId"], tags, self.get_zone_name(zoneId))

return get_model_infos(-1)
return get_model_infos(-1, [])

def get_serial_number(self, deviceId: int | None = None) -> str:
"""Get serial number."""
Expand All @@ -374,7 +376,7 @@ def get_capabilities_for_device(self, deviceId: int | None = None):
capabilities = []
for dev in self._devices:
if dev["deviceId"] == deviceId:
modelInfos = get_model_infos(dev["modelId"])
modelInfos = get_model_infos(dev["modelId"], dev["tags"])
for capability in dev["capabilities"]:
capability_infos = get_capability_infos(
modelInfos,
Expand Down
145 changes: 117 additions & 28 deletions custom_components/cozytouch/model.py
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@
""" # noqa: D205

from enum import StrEnum
import logging

from homeassistant.components.climate import HVACMode
from homeassistant.components.climate.const import (
Expand Down Expand Up @@ -54,7 +55,7 @@ class CozytouchDeviceType(StrEnum):
HUB = "hub"


def get_model_infos(modelId: int, zoneName: str | None = None):
def get_model_infos(modelId: int, tags: list, zoneName: str | None = None) -> dict:
"""Return infos from model ID."""
modelInfos = {"modelId": modelId, "HVACModesCapabilityId": {7, 8}}

Expand Down Expand Up @@ -186,38 +187,114 @@ def get_model_infos(modelId: int, zoneName: str | None = None):
}

elif modelId >= 557 and modelId <= 561:
name = "Air Conditioner "
name = "Unknown product (" + str(modelId) + ")"
modelInfos["type"] = CozytouchDeviceType.UNKNOWN
modelInfos["HVACModes"] = {
0: HVACMode.OFF,
4: HVACMode.HEAT,
}

childrenIds = []
for tag in tags if tags is not None else []:
if (
"label" in tag
and tag["label"] == "iothubChildrenIds"
and "value" in tag
):
childrenIds = tag["value"].split(",")
break

if any(childId.startswith("UI_") for childId in childrenIds): # Air conditioner detected
name = "Air Conditioner "
modelInfos["type"] = CozytouchDeviceType.AC
modelInfos["currentTemperatureAvailable"] = False
modelInfos["quietModeAvailable"] = True

modelInfos["fanModes"] = {
1: FAN_LOW,
2: FAN_MEDIUM,
3: FAN_HIGH,
5: FAN_AUTO,
}

modelInfos["swingModes"] = {
1: SWING_MODE_UP,
2: SWING_MODE_MIDDLE_UP,
3: SWING_MODE_MIDDLE_DOWN,
4: SWING_MODE_DOWN,
}

modelInfos["HVACModes"] = {
0: HVACMode.OFF,
1: HVACMode.AUTO,
3: HVACMode.COOL,
4: HVACMode.HEAT,
7: HVACMode.FAN_ONLY,
8: HVACMode.DRY,
}
elif any(childId.startswith("THZONE_") for childId in childrenIds): # Thermostat detected
# NOTE: not sure about the name here as we are indeed controlling the Thermostat setting but this in-turn activate underfloor heating.
name = "Thermostat "
if zoneName is not None:
modelInfos["name"] = name + "(" + zoneName + ")"
else:
modelInfos["name"] = name + "(#" + str(modelId - 556) + ")"

modelInfos["type"] = CozytouchDeviceType.THERMOSTAT
modelInfos["currentTemperatureAvailable"] = True
modelInfos["currentTemperatureAvailableZ1"] = True
modelInfos["currentTemperatureAvailableZ2"] = False
modelInfos["overrideModeAvailable"] = True
modelInfos["quietModeAvailable"] = False

modelInfos["HVACModes"] = {
0: HVACMode.OFF,
4: HVACMode.HEAT,
}
modelInfos["HeatingModes"] = {
0: HEATING_MODE_MANUAL,
3: HEATING_MODE_ECO_PLUS,
4: HEATING_MODE_PROG,
}
else: # Fallback to AC if none found to keep backward compatibility
name = "Air Conditioner "
if zoneName is not None:
modelInfos["name"] = name + "(" + zoneName + ")"
else:
modelInfos["name"] = name + "(#" + str(modelId - 556) + ")"

modelInfos["type"] = CozytouchDeviceType.AC
modelInfos["currentTemperatureAvailable"] = False
modelInfos["quietModeAvailable"] = True

modelInfos["fanModes"] = {
1: FAN_LOW,
2: FAN_MEDIUM,
3: FAN_HIGH,
5: FAN_AUTO,
}

modelInfos["swingModes"] = {
1: SWING_MODE_UP,
2: SWING_MODE_MIDDLE_UP,
3: SWING_MODE_MIDDLE_DOWN,
4: SWING_MODE_DOWN,
}

modelInfos["HVACModes"] = {
0: HVACMode.OFF,
1: HVACMode.AUTO,
3: HVACMode.COOL,
4: HVACMode.HEAT,
7: HVACMode.FAN_ONLY,
8: HVACMode.DRY,
}

if zoneName is not None:
modelInfos["name"] = name + "(" + zoneName + ")"
else:
modelInfos["name"] = name + "(#" + str(modelId - 556) + ")"

modelInfos["type"] = CozytouchDeviceType.AC
modelInfos["currentTemperatureAvailable"] = False
modelInfos["quietModeAvailable"] = True

modelInfos["fanModes"] = {
1: FAN_LOW,
2: FAN_MEDIUM,
3: FAN_HIGH,
5: FAN_AUTO,
}

modelInfos["swingModes"] = {
1: SWING_MODE_UP,
2: SWING_MODE_MIDDLE_UP,
3: SWING_MODE_MIDDLE_DOWN,
4: SWING_MODE_DOWN,
}

modelInfos["HVACModes"] = {
0: HVACMode.OFF,
1: HVACMode.AUTO,
3: HVACMode.COOL,
4: HVACMode.HEAT,
7: HVACMode.FAN_ONLY,
8: HVACMode.DRY,
}

elif modelId >= 562 and modelId <= 570:
name = "Air Conditioner User Interface "
Expand Down Expand Up @@ -298,6 +375,18 @@ def get_model_infos(modelId: int, zoneName: str | None = None):
4: HVACMode.HEAT,
}

elif modelId >= 1505 and modelId <= 1513:
name = "Thermostat Thermal Zone "
if zoneName is not None:
modelInfos["name"] = name + "(" + zoneName + ")"
else:
modelInfos["name"] = name + "(#" + str(modelId - 1504) + ")"

modelInfos["type"] = CozytouchDeviceType.THERMOSTAT
modelInfos["HVACModes"] = {
0: HVACMode.OFF,
}

elif modelId == 1543:
modelInfos["name"] = "Asama Connecté II Ventilo 1750W Blanc"
modelInfos["type"] = CozytouchDeviceType.TOWEL_RACK
Expand Down