Skip to content

Commit

Permalink
add support for EEV coils + add outdoor unit heater sensors (#21)
Browse files Browse the repository at this point in the history
  • Loading branch information
zlangbert authored Feb 11, 2024
1 parent 9aeda87 commit 789541b
Show file tree
Hide file tree
Showing 2 changed files with 153 additions and 6 deletions.
43 changes: 43 additions & 0 deletions custom_components/daikinone/daikinone.py
Original file line number Diff line number Diff line change
Expand Up @@ -65,6 +65,12 @@ class DaikinOutdoorUnitReversingValveStatus(Enum):
UNKNOWN = 255


class DaikinOutdoorUnitHeaterStatus(Enum):
OFF = 0
ON = 1
UNKNOWN = 255


@dataclass
class DaikinOutdoorUnit(DaikinEquipment):
inverter_software_version: str | None
Expand Down Expand Up @@ -92,10 +98,24 @@ class DaikinOutdoorUnit(DaikinEquipment):
compressor_amps: float
inverter_amps: float
fan_motor_amps: float
crank_case_heater: DaikinOutdoorUnitHeaterStatus
drain_pan_heater: DaikinOutdoorUnitHeaterStatus
preheat_heater: DaikinOutdoorUnitHeaterStatus

# needs confirmation on unit in raw data
# preheat_output_watts: int | None

# compressor reduction mode - ctOutdoorCompressorReductionMode - 1=off, ?


@dataclass
class DaikinEEVCoil(DaikinEquipment):
indoor_superheat_temperature: Temperature
liquid_temperature: Temperature
suction_temperature: Temperature
pressure_psi: int


class DaikinThermostatCapability(Enum):
HEAT = auto()
COOL = auto()
Expand Down Expand Up @@ -361,6 +381,29 @@ def __map_equipment(self, payload: DaikinDeviceDataResponse) -> dict[str, Daikin
compressor_amps=payload.data["ctCompressorCurrent"] / 10,
inverter_amps=payload.data["ctInverterCurrent"] / 10,
fan_motor_amps=payload.data["ctODFanMotorCurrent"] / 10,
crank_case_heater=DaikinOutdoorUnitHeaterStatus(payload.data["ctCrankCaseHeaterOnOff"]),
drain_pan_heater=DaikinOutdoorUnitHeaterStatus(payload.data["ctDrainPanHeaterOnOff"]),
preheat_heater=DaikinOutdoorUnitHeaterStatus(payload.data["ctPreHeatOnOff"]),
)

# eev coil
if payload.data["ctCoilUnitType"] < 255:
model = "EEV Coil"
serial = payload.data["ctCoilSerialNoCharacter1_15"].strip()
eid = f"eevcoil-{serial}"
name = "EEV Coil"

equipment[eid] = DaikinEEVCoil(
id=eid,
thermostat_id=payload.id,
name=name,
model=model,
serial=serial,
firmware_version=payload.data["ctCoilControlSoftwareVersion"].strip(),
pressure_psi=payload.data["ctEEVCoilPressureSensor"],
indoor_superheat_temperature=Temperature.from_fahrenheit(payload.data["ctEEVCoilSuperHeatValue"] / 10),
liquid_temperature=Temperature.from_fahrenheit(payload.data["ctEEVCoilSubCoolValue"] / 10),
suction_temperature=Temperature.from_fahrenheit(payload.data["ctEEVCoilSuctionTemperature"] / 10),
)

return equipment
Expand Down
116 changes: 110 additions & 6 deletions custom_components/daikinone/sensor.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import logging
from typing import Callable, TypeVar
from typing import Callable

from homeassistant.components.sensor import SensorEntity, SensorEntityDescription, SensorDeviceClass, SensorStateClass
from homeassistant.config_entries import ConfigEntry
Expand All @@ -21,7 +21,9 @@
from custom_components.daikinone.const import MANUFACTURER
from custom_components.daikinone.daikinone import (
DaikinDevice,
DaikinEEVCoil,
DaikinOutdoorUnitReversingValveStatus,
DaikinOutdoorUnitHeaterStatus,
DaikinThermostat,
DaikinIndoorUnit,
DaikinEquipment,
Expand Down Expand Up @@ -91,7 +93,7 @@ async def async_setup_entry(
# equipment sensors
for equipment in thermostat.equipment.values():
match equipment:
# air handler sensors
# air handler / furnance sensors
case DaikinIndoorUnit():
entities += [
DaikinOneEquipmentSensor(
Expand Down Expand Up @@ -582,6 +584,111 @@ async def async_setup_entry(
)
)

if equipment.crank_case_heater is not DaikinOutdoorUnitHeaterStatus.UNKNOWN:
entities.append(
DaikinOneEquipmentSensor(
description=SensorEntityDescription(
key="crank_case_heater",
name="Crrank Case Heater",
has_entity_name=True,
device_class=SensorDeviceClass.ENUM,
),
data=data,
device=equipment,
attribute=lambda e: e.crank_case_heater.name.capitalize(),
)
)

if equipment.drain_pan_heater is not DaikinOutdoorUnitHeaterStatus.UNKNOWN:
entities.append(
DaikinOneEquipmentSensor(
description=SensorEntityDescription(
key="drain_pan_heater",
name="Drain Pan Heater",
has_entity_name=True,
device_class=SensorDeviceClass.ENUM,
),
data=data,
device=equipment,
attribute=lambda e: e.drain_pan_heater.name.capitalize(),
)
)

if equipment.preheat_heater is not DaikinOutdoorUnitHeaterStatus.UNKNOWN:
entities.append(
DaikinOneEquipmentSensor(
description=SensorEntityDescription(
key="preheat_heater",
name="Preheat",
has_entity_name=True,
device_class=SensorDeviceClass.ENUM,
),
data=data,
device=equipment,
attribute=lambda e: e.preheat_heater.name.capitalize(),
)
)

case DaikinEEVCoil():
entities += [
DaikinOneEquipmentSensor(
description=SensorEntityDescription(
key="indoor_superheat_temperature",
name="Indoor Superheat Temperature",
has_entity_name=True,
state_class=SensorStateClass.MEASUREMENT,
device_class=SensorDeviceClass.TEMPERATURE,
native_unit_of_measurement=UnitOfTemperature.CELSIUS,
icon="mdi:thermometer",
),
data=data,
device=equipment,
attribute=lambda e: e.indoor_superheat_temperature.celsius,
),
DaikinOneEquipmentSensor(
description=SensorEntityDescription(
key="liquid_temperature",
name="Liquid Temperature",
has_entity_name=True,
state_class=SensorStateClass.MEASUREMENT,
device_class=SensorDeviceClass.TEMPERATURE,
native_unit_of_measurement=UnitOfTemperature.CELSIUS,
icon="mdi:thermometer",
),
data=data,
device=equipment,
attribute=lambda e: e.liquid_temperature.celsius,
),
DaikinOneEquipmentSensor(
description=SensorEntityDescription(
key="suction_temperature",
name="Suction Temperature",
has_entity_name=True,
state_class=SensorStateClass.MEASUREMENT,
device_class=SensorDeviceClass.TEMPERATURE,
native_unit_of_measurement=UnitOfTemperature.CELSIUS,
icon="mdi:thermometer",
),
data=data,
device=equipment,
attribute=lambda e: e.suction_temperature.celsius,
),
DaikinOneEquipmentSensor(
description=SensorEntityDescription(
key="pressure",
name="Pressure",
has_entity_name=True,
state_class=SensorStateClass.MEASUREMENT,
device_class=SensorDeviceClass.PRESSURE,
native_unit_of_measurement=UnitOfPressure.PSI,
icon="mdi:pipe",
),
data=data,
device=equipment,
attribute=lambda e: e.pressure_psi,
),
]

case _:
log.warning(f"unexpected equipment: {equipment}")

Expand Down Expand Up @@ -650,10 +757,7 @@ async def async_update(self) -> None:
self._attr_native_value = self._attribute(self._device)


E = TypeVar("E", bound=DaikinEquipment)


class DaikinOneEquipmentSensor(DaikinOneSensor[E]):
class DaikinOneEquipmentSensor[E: DaikinEquipment](DaikinOneSensor[E]):
def __init__(
self, description: SensorEntityDescription, data: DaikinOneData, device: E, attribute: Callable[[E], StateType]
) -> None:
Expand Down

0 comments on commit 789541b

Please sign in to comment.