Skip to content

Commit

Permalink
Merge pull request #7 from IngoS11/v0.1.8
Browse files Browse the repository at this point in the history
V0.1.8
  • Loading branch information
IngoS11 committed Jul 8, 2022
2 parents e594919 + 86994b2 commit d103494
Show file tree
Hide file tree
Showing 4 changed files with 211 additions and 13 deletions.
63 changes: 56 additions & 7 deletions custom_components/schluter/climate.py
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,12 @@
SchluterApi,
Thermostat,
)
from aioschluter.const import (
REGULATION_MODE_MANUAL,
REGULATION_MODE_SCHEDULE,
REGULATION_MODE_AWAY,
)

from homeassistant.components.climate import ClimateEntity
from homeassistant.components.climate.const import (
ClimateEntityFeature,
Expand Down Expand Up @@ -50,8 +56,7 @@ async def async_setup_entry(
class SchluterThermostat(CoordinatorEntity[DataUpdateCoordinator], ClimateEntity):
"""Define an Schluter Thermostat Entity."""

_attr_hvac_mode = HVACMode.HEAT
_attr_hvac_modes = [HVACMode.HEAT]
_attr_hvac_modes = [HVACMode.HEAT, HVACMode.AUTO, HVACMode.OFF]
_attr_supported_features = ClimateEntityFeature.TARGET_TEMPERATURE

coordinator: DataUpdateCoordinator[dict[str, dict[str, Thermostat]]]
Expand Down Expand Up @@ -82,6 +87,22 @@ def device_info(self):
"manufacturer": "Schluter",
}

@property
def hvac_mode(self):
if (
self.coordinator.data[self._attr_unique_id].regulation_mode
== REGULATION_MODE_SCHEDULE
):
self._attr_hvac_mode = HVACMode.AUTO
elif (
self.coordinator.data[self._attr_unique_id].regulation_mode
== REGULATION_MODE_MANUAL
):
self._attr_hvac_mode = HVACMode.HEAT
else:
self._attr_hvac_mode = HVACMode.OFF
return self._attr_hvac_mode

@property
def unique_id(self):
"""Return unique ID for this device."""
Expand Down Expand Up @@ -124,28 +145,56 @@ def max_temp(self):
"""Identify max_temp in Schluter API."""
return self.coordinator.data[self._attr_unique_id].max_temp

async def async_set_hvac_mode(self, hvac_mode: HVACMode) -> None:
"""Mode is always heating, so do nothing."""

# This property is important to let HA know if this entity is online or not.
# If an entity is offline (return False), the UI will refelect this.
@property
def available(self) -> bool:
"""Return True if roller and hub is available."""
return self.coordinator.data[self._attr_unique_id].is_online

async def async_set_hvac_mode(self, hvac_mode: HVACMode) -> None:
"""Set the hvac mode"""
if hvac_mode == self._attr_hvac_mode:
return

serial_number = self.coordinator.data[self._attr_unique_id].serial_number
_LOGGER.debug(
"Setting HVAC mode of thermostat: %s to: %s", self._name, hvac_mode
)

if hvac_mode == HVACMode.AUTO:
regulation_mode = REGULATION_MODE_SCHEDULE
elif hvac_mode == HVACMode.HEAT:
regulation_mode = REGULATION_MODE_MANUAL
else:
regulation_mode = REGULATION_MODE_AWAY

try:
await self._api.async_set_regulation_mode(
self._api.sessionid, serial_number, regulation_mode
)
self._attr_hvac_mode = hvac_mode
await self.coordinator.async_request_refresh()
except (
InvalidUserPasswordError,
InvalidSessionIdError,
) as err:
raise ConfigEntryAuthFailed from err
except (ApiError, ClientConnectorError) as err:
raise UpdateFailed(err) from err

async def async_set_temperature(self, **kwargs):
"""Set new target temperature."""
target_temp = kwargs.get(ATTR_TEMPERATURE)
serial_number = self.coordinator.data[self._attr_unique_id].serial_number
session_id = self._api.sessionid
_LOGGER.debug("Setting thermostat temperature: %s", target_temp)

try:
if target_temp is not None:
await self._api.async_set_temperature(
session_id, serial_number, target_temp
self._api.sessionid, serial_number, target_temp
)
self._attr_hvac_mode = HVACMode.HEAT
except (
InvalidUserPasswordError,
InvalidSessionIdError,
Expand Down
2 changes: 2 additions & 0 deletions custom_components/schluter/const.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,3 +2,5 @@

DOMAIN = "schluter"
ZERO_WATTS = 0
PRESET_MANUAL = "On Manual"
PRESET_SCHEDULE = "On Schedule"
4 changes: 2 additions & 2 deletions custom_components/schluter/manifest.json
Original file line number Diff line number Diff line change
@@ -1,12 +1,12 @@
{
"version": "0.1.7",
"version": "0.1.8",
"domain": "schluter",
"name": "Schluter DITRA-HEAT-E-Wifi",
"config_flow": true,
"documentation": "https://github.com/Ingos11/ha-schluter",
"issue_tracker": "https://github.com/Ingos11/ha-schluter/issues",
"requirements": [
"aioschluter==0.1.5"
"aioschluter==0.1.9"
],
"ssdp": [],
"zeroconf": [],
Expand Down
155 changes: 151 additions & 4 deletions custom_components/schluter/sensor.py
Original file line number Diff line number Diff line change
@@ -1,12 +1,11 @@
"""Break out the temperature of the thermostat into a separate sensor entity."""

from aioschluter import Thermostat
from homeassistant.components.sensor import (
SensorDeviceClass,
SensorEntity,
SensorStateClass,
)
from homeassistant.const import POWER_WATT, TEMP_CELSIUS
from homeassistant.const import ENERGY_KILO_WATT_HOUR, POWER_WATT, TEMP_CELSIUS
from homeassistant.helpers.update_coordinator import (
CoordinatorEntity,
DataUpdateCoordinator,
Expand All @@ -20,17 +19,77 @@ async def async_setup_entry(hass, config_entry, async_add_entities):
"""Add sensors for passed config_entry in HA."""
data: SchluterData = hass.data[DOMAIN][config_entry.entry_id]

# Add the Temperature Sensor
async_add_entities(
SchluterTemperatureSensor(data.coordinator, thermostat_id)
for thermostat_id in data.coordinator.data
)

# Add the Target Temperature Sensor
async_add_entities(
SchluterTargetTemperatureSensor(data.coordinator, thermostat_id)
for thermostat_id in data.coordinator.data
)

# Add the Power Sensor
async_add_entities(
SchluterPowerSensor(data.coordinator, thermostat_id)
for thermostat_id in data.coordinator.data
)

# Add the price per kwh Sensor
async_add_entities(
SchluterTemperatureSensor(data.coordinator, thermostat_id)
SchluterEnergyPriceSensor(data.coordinator, thermostat_id)
for thermostat_id in data.coordinator.data
)

# Add the virtual/calculated KwH Sensor
async_add_entities(
SchluterEnergySensor(data.coordinator, thermostat_id)
for thermostat_id in data.coordinator.data
)


class SchluterTargetTemperatureSensor(
CoordinatorEntity[DataUpdateCoordinator], SensorEntity
):
"""Representation of a Sensor."""

_attr_native_unit_of_measurement = TEMP_CELSIUS
_attr_device_class = SensorDeviceClass.TEMPERATURE
_attr_state_class = SensorStateClass.MEASUREMENT

def __init__(
self,
coordinator: DataUpdateCoordinator[dict[str, dict[str, Thermostat]]],
thermostat_id: str,
) -> None:
"""Initialize the sensor."""
super().__init__(coordinator)
self._attr_name = coordinator.data[thermostat_id].name + " Target Temperature"
self._thermostat_id = thermostat_id
self._attr_unique_id = (
f"{coordinator.data[thermostat_id].name}-target-{self._attr_device_class}"
)

@property
def available(self) -> bool:
"""Return True if Schluter thermostat is available."""
return self.coordinator.data[self._thermostat_id].is_online

@property
def device_info(self):
"""Return information to link this entity."""
return {
"identifiers": {(DOMAIN, self._thermostat_id)},
}

@property
def native_value(self) -> float:
"""Return the state of the sensor."""
return self.coordinator.data[self._thermostat_id].set_point_temp


class SchluterTemperatureSensor(CoordinatorEntity[DataUpdateCoordinator], SensorEntity):
"""Representation of a Sensor."""

Expand Down Expand Up @@ -106,5 +165,93 @@ def native_value(self) -> int:
"""Return the state of the sensor."""
if self.coordinator.data[self._thermostat_id].is_heating:
return self.coordinator.data[self._thermostat_id].load_measured_watt
return ZERO_WATTS


class SchluterEnergySensor(CoordinatorEntity[DataUpdateCoordinator], SensorEntity):
"""Representation of a PowerSensor."""

_attr_native_unit_of_measurement = ENERGY_KILO_WATT_HOUR
_attr_device_class = SensorDeviceClass.ENERGY
_attr_state_class = SensorStateClass.TOTAL

def __init__(
self,
coordinator: DataUpdateCoordinator[dict[str, dict[str, Thermostat]]],
thermostat_id: str,
values=60,
) -> None:
"""Initialize the sensor."""
super().__init__(coordinator)
self._attr_name = coordinator.data[thermostat_id].name + " Energy"
self._thermostat_id = thermostat_id
self._attr_unique_id = (
f"{coordinator.data[thermostat_id].name}-{self._attr_device_class}"
)
self._wattage_list = []
self._values = values

def add(self, watt):
"""Queue a number wattage for kwh calculation."""
self._wattage_list.insert(0, watt)
if len(self._wattage_list) == self._values:
self._wattage_list.pop()

@property
def available(self) -> bool:
"""Return True if Schluter thermostat is available."""
return self.coordinator.data[self._thermostat_id].is_online

@property
def device_info(self):
"""Return information to link this entity."""
return {
"identifiers": {(DOMAIN, self._thermostat_id)},
}

@property
def native_value(self) -> float:
"""Return the state of the sensor."""
if self.coordinator.data[self._thermostat_id].is_heating:
self.add(self.coordinator.data[self._thermostat_id].load_measured_watt)
else:
return ZERO_WATTS
self.add(ZERO_WATTS)
return round((sum(self._wattage_list) / self._values) / 1000, 2)


class SchluterEnergyPriceSensor(CoordinatorEntity[DataUpdateCoordinator], SensorEntity):
"""Representation of a Sensor."""

_attr_native_unit_of_measurement = "$/kWh"
_attr_device_class = SensorDeviceClass.MONETARY
_attr_state_class = SensorStateClass.MEASUREMENT

def __init__(
self,
coordinator: DataUpdateCoordinator[dict[str, dict[str, Thermostat]]],
thermostat_id: str,
) -> None:
"""Initialize the sensor."""
super().__init__(coordinator)
self._attr_name = coordinator.data[thermostat_id].name + " Price"
self._thermostat_id = thermostat_id
self._attr_unique_id = (
f"{coordinator.data[thermostat_id].name}-{self._attr_device_class}"
)

@property
def available(self) -> bool:
"""Return True if Schluter thermostat is available."""
return self.coordinator.data[self._thermostat_id].is_online

@property
def device_info(self):
"""Return information to link this entity."""
return {
"identifiers": {(DOMAIN, self._thermostat_id)},
}

@property
def native_value(self) -> float:
"""Return the state of the sensor."""
return self.coordinator.data[self._thermostat_id].kwh_charge

0 comments on commit d103494

Please sign in to comment.