Skip to content
This repository has been archived by the owner on Mar 19, 2024. It is now read-only.

Commit

Permalink
Merge pull request #4 from thomasgermain/dev
Browse files Browse the repository at this point in the history
Release 1.1.0
  • Loading branch information
thomasgermain authored Apr 3, 2020
2 parents 821af33 + a1d6604 commit 1bb490f
Show file tree
Hide file tree
Showing 13 changed files with 365 additions and 354 deletions.
11 changes: 8 additions & 3 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -12,13 +12,19 @@ You have to provided your username and password (same as multimatic app)
## Releases
### [1.0.0](https://github.com/thomasgermain/vaillant-component/releases/tag/1.0.0)
First release using config flow
### [1.1.0](https://github.com/thomasgermain/vaillant-component/releases/tag/1.1.0)
- Move everything to async
- Bugfix for circulation (no considered `on` when hot water boost was activated)
- Removed boiler temperature and boiler water pressure in favor of `report` entity (breaking change)
- Better error handling
- Automatic re-authentication in case of error


## Provided entities
- 1 water_heater entity, if any water heater: `water_heater.vaillant_<water heater id>`, basically `water_heater.vaillant_control_dhw`
- 1 climate entity per zone (expect if the zone is controlled by room) `climate.vaillant_<zone id>`
- 1 climate entity per room `climate.vaillant_<room name>`
- 1 binary_sensor entity `binary_sensor.vaillant_circulation` reflecting if the circulation is on or off
- 1 binary_sensor entity `binary_sensor.vaillant_control_dhw` reflecting if the circulation is on or off
- 1 binary_sensor entity `climate.vaillant_<room name>_window` per room reflecting the state of the "open window" in a room (this is a feature of the vaillant API, if the temperature is going down pretty fast, the API assumes there is an open window and heating stops)
- 1 binary_sensor entity `climate.vaillant_<sgtin>_lock`per device reflecting if valves are "child locked" or not
- 1 binary_sensor entity `binary_sensor.vaillant_<sgtin>_battery` reflecting battery level for each device (VR50, VR51) in the system
Expand All @@ -27,8 +33,7 @@ First release using config flow
- 1 binary_sensor entity `binary_sensor.vaillant_system_online` to know if the vr900/920 is connected to the internet
- 1 binary_sensor entity `binary_sensor.vaillant_<boiler model>` to know if there is an error at the boiler. **Some boiler does not provide this information, so entity won't be available.**
- 1 temperature sensor `sensor.vaillant_outdoor_temperature` for outdoor temperature
- 1 sensor for water `sensor.vaillant_boiler_pressure` pressure in boiler
- 1 temperature sensor ` sensor.vaillant_boiler_temperature` for water temperature in boiler
- 1 sensor for each report in live_report
- 1 binary sensor `binary_sensor.vaillant_quick_mode` to know a quick mode is running on
- 1 binary sensor ` binary_sensor.vaillant_holiday` to know the holiday mode is on/off
- dynamic binary sensors if there are extra errors coming from the api.
Expand Down
8 changes: 6 additions & 2 deletions vaillant/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,8 @@ async def async_setup_entry(hass: HomeAssistant, entry: ConfigEntry):
username = entry.data[CONF_USERNAME]
password = entry.data[CONF_PASSWORD]
api: ApiHub = ApiHub(hass, username, password)
api.update_system()
await api.authenticate()
await api.update_system()

hass.data[DOMAIN] = DomainData(api, entry)

Expand All @@ -40,7 +41,10 @@ async def async_setup_entry(hass: HomeAssistant, entry: ConfigEntry):
method = getattr(service_handler, method_name)
hass.services.async_register(DOMAIN, vaillant_service, method, schema=schema)

hass.bus.async_listen_once(EVENT_HOMEASSISTANT_STOP, lambda event: api.logout())
async def logout(param):
await api.logout()

hass.bus.async_listen_once(EVENT_HOMEASSISTANT_STOP, logout)

return True

Expand Down
83 changes: 32 additions & 51 deletions vaillant/binary_sensor.py
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,6 @@
QuickMode,
Room,
SystemInfo,
SystemStatus,
)

from homeassistant.components.binary_sensor import (
Expand All @@ -27,6 +26,7 @@
)
from homeassistant.util import Throttle

from . import ApiHub
from .const import DEFAULT_SCAN_INTERVAL, DOMAIN as VAILLANT
from .entities import (
VaillantBoilerDevice,
Expand All @@ -41,17 +41,17 @@
async def async_setup_entry(hass, entry, async_add_entities):
"""Set up the Vaillant binary sensor platform."""
sensors = []
hub = hass.data[VAILLANT].api
hub: ApiHub = hass.data[VAILLANT].api
if hub.system:
if hub.system.circulation:
sensors.append(CirculationSensor(hub.system.circulation))
if hub.system.dhw and hub.system.dhw.circulation:
sensors.append(CirculationSensor(hub.system.dhw.circulation))

if hub.system.boiler_status:
sensors.append(BoilerError(hub.system.boiler_status))

if hub.system.system_status:
sensors.append(BoxOnline(hub.system.system_status, hub.system.system_info))
sensors.append(BoxUpdate(hub.system.system_status, hub.system.system_info))
if hub.system.info:
sensors.append(BoxOnline(hub.system.info))
sensors.append(BoxUpdate(hub.system.info))

for room in hub.system.rooms:
sensors.append(RoomWindow(room))
Expand All @@ -62,7 +62,7 @@ async def async_setup_entry(hass, entry, async_add_entities):
sensors.append(RoomDeviceBattery(device, room))
sensors.append(RoomDeviceConnectivity(device, room))

entity = HolidayModeSensor(hub.system.holiday_mode)
entity = HolidayModeSensor(hub.system.holiday)
sensors.append(entity)

entity = QuickModeSensor(hub.system.quick_mode)
Expand All @@ -75,7 +75,7 @@ async def async_setup_entry(hass, entry, async_add_entities):

_LOGGER.info("Adding %s binary sensor entities", len(sensors))

async_add_entities(sensors)
async_add_entities(sensors, True)
return True


Expand All @@ -85,20 +85,20 @@ class CirculationSensor(VaillantEntity, BinarySensorDevice):
def __init__(self, circulation: Circulation):
"""Initialize entity."""
super().__init__(
DOMAIN, DEVICE_CLASS_POWER, circulation.name, circulation.name, False
DOMAIN, DEVICE_CLASS_POWER, circulation.id, circulation.name, False
)
self._circulation = circulation
self._active_mode = None

@property
def is_on(self):
"""Return true if the binary sensor is on."""
from pymultimatic.model import OperatingModes, SettingModes, QuickModes

active_mode = self._circulation.active_mode
return (
active_mode.current_mode == OperatingModes.ON
or active_mode.sub_mode == SettingModes.ON
or active_mode == QuickModes.HOTWATER_BOOST
self._active_mode.current == OperatingModes.ON
or self._active_mode.sub == SettingModes.ON
or self._active_mode.current == QuickModes.HOTWATER_BOOST
)

@property
Expand All @@ -108,17 +108,8 @@ def available(self):

async def vaillant_update(self):
"""Update specific for vaillant."""
new_circulation = self.hub.find_component(self._circulation)

if new_circulation:
_LOGGER.debug(
"New / old state: %s / %s",
new_circulation.active_mode.current_mode,
self._circulation.active_mode.current_mode,
)
else:
_LOGGER.debug("Circulation %s doesn't exist anymore", self._circulation.id)
self._circulation = new_circulation
self._circulation = self.hub.system.dhw.circulation
self._active_mode = self.hub.system.get_active_mode_circulation()


class RoomWindow(VaillantEntity, BinarySensorDevice):
Expand Down Expand Up @@ -259,67 +250,57 @@ def is_on(self):
class BaseVaillantSystem(VaillantEntity, VaillantBoxDevice, BinarySensorDevice):
"""Base class for system wide binary sensor."""

def __init__(
self, status: SystemStatus, info: SystemInfo, device_class, name, comp_id
):
def __init__(self, info: SystemInfo, device_class, name, comp_id):
"""Initialize entity."""
VaillantEntity.__init__(self, DOMAIN, device_class, name, comp_id, False)
VaillantBoxDevice.__init__(self, info, status)
VaillantBoxDevice.__init__(self, info)

@property
def available(self):
"""Return True if entity is available."""
return self.system_status is not None
return self.system_info is not None

async def vaillant_update(self):
"""Update specific for vaillant."""
system_status: SystemStatus = self.hub.system.system_status
system_info: SystemInfo = self.hub.system.info

if system_status:
if system_info:
_LOGGER.debug(
"Found new system status " "online? %s, up to date? %s",
system_status.is_online,
system_status.is_up_to_date,
system_info.is_online,
system_info.is_up_to_date,
)
else:
_LOGGER.debug("System status doesn't exist anymore")
self.system_status = system_status
self.system_info = self.hub.system.system_info
self.system_info = system_info


class BoxUpdate(BaseVaillantSystem):
"""Update binary sensor."""

def __init__(self, status: SystemStatus, info: SystemInfo):
def __init__(self, info: SystemInfo):
"""Init."""
super().__init__(
status, info, DEVICE_CLASS_POWER, "System update", "system_update"
)
super().__init__(info, DEVICE_CLASS_POWER, "System update", "system_update")

@property
def is_on(self):
"""Return true if the binary sensor is on."""
return not self.system_status.is_up_to_date
return not self.system_info.is_up_to_date


class BoxOnline(BaseVaillantSystem):
"""Check if box is online."""

def __init__(self, status: SystemStatus, info: SystemInfo):
def __init__(self, info: SystemInfo):
"""Init."""
BaseVaillantSystem.__init__(
self,
status,
info,
DEVICE_CLASS_CONNECTIVITY,
"System Online",
"system_online",
self, info, DEVICE_CLASS_CONNECTIVITY, "System Online", "system_online",
)

@property
def is_on(self):
"""Return true if the binary sensor is on."""
return self.system_status.is_online
return self.system_info.is_online


class BoilerError(VaillantEntity, BinarySensorDevice, VaillantBoilerDevice):
Expand Down Expand Up @@ -457,13 +438,13 @@ def state_attributes(self):
return {
"start_date": self._holiday.start_date.isoformat(),
"end_date": self._holiday.end_date.isoformat(),
"temperature": self._holiday.target_temperature,
"temperature": self._holiday.target,
}
return {}

async def vaillant_update(self):
"""Update specific for vaillant."""
self._holiday = self.hub.system.holiday_mode
self._holiday = self.hub.system.holiday


class QuickModeSensor(VaillantEntity, BinarySensorDevice):
Expand Down
61 changes: 36 additions & 25 deletions vaillant/climate.py
Original file line number Diff line number Diff line change
Expand Up @@ -91,26 +91,19 @@ def temperature_unit(self):
@property
def target_temperature(self):
"""Return the temperature we try to reach."""
_LOGGER.debug("Target temp is %s", self.active_mode.target_temperature)
return self.active_mode.target_temperature
_LOGGER.debug("Target temp is %s", self.active_mode.target)
return self.active_mode.target

@property
def current_temperature(self):
"""Return the current temperature."""
return self.component.current_temperature
return self.component.temperature

@property
def is_aux_heat(self) -> Optional[bool]:
"""Return true if aux heater."""
return False

@property
def state_attributes(self) -> Dict[str, Any]:
"""Return the optional state attributes."""
attributes = super().state_attributes
attributes.update(gen_state_attrs(self.component, self.active_mode))
return attributes

@property
def fan_mode(self) -> Optional[str]:
"""Return the fan setting."""
Expand Down Expand Up @@ -165,10 +158,10 @@ def target_temperature_low(self) -> Optional[float]:
def hvac_mode(self) -> str:
"""Return hvac operation ie. heat, cool mode."""

hvac_mode = self.mode_to_hvac(self.active_mode.current_mode)
hvac_mode = self.mode_to_hvac(self.active_mode.current)

if hvac_mode is None:
if self.active_mode.current_mode in [
if self.active_mode.current in [
OperatingModes.QUICK_VETO,
OperatingModes.MANUAL,
]:
Expand All @@ -178,7 +171,7 @@ def hvac_mode(self) -> str:
hvac_mode = HVAC_MODE_COOL
else:
_LOGGER.warning(
"Unknown mode %s, will return None", self.active_mode.current_mode
"Unknown mode %s, will return None", self.active_mode.current
)
return hvac_mode

Expand All @@ -192,7 +185,7 @@ def _refresh(self, system, component):
self.component = component

def _is_heating(self):
return self.active_mode.target_temperature > self.component.current_temperature
return self.active_mode.target > self.component.temperature

@abc.abstractmethod
def mode_to_hvac(self, mode):
Expand Down Expand Up @@ -260,19 +253,28 @@ def active_mode(self) -> ActiveMode:
"""Get active mode of the climate."""
return self._system.get_active_mode_room(self.component)

def set_temperature(self, **kwargs):
async def async_set_temperature(self, **kwargs):
"""Set new target temperature."""
self.hub.set_room_target_temperature(
self, self.component, float(kwargs.get(ATTR_TEMPERATURE))
await self.hub.set_room_target_temperature(
self, float(kwargs.get(ATTR_TEMPERATURE))
)

def set_preset_mode(self, preset_mode: str) -> None:
"""Set new preset mode."""

def set_hvac_mode(self, hvac_mode: str) -> None:
async def async_set_hvac_mode(self, hvac_mode: str) -> None:
"""Set new target hvac mode."""
mode = self._HVAC_TO_MODE[hvac_mode]
self.hub.set_room_operating_mode(self, self.component, mode)
await self.hub.set_room_operating_mode(self, self.component, mode)

@property
def state_attributes(self) -> Dict[str, Any]:
"""Return the optional state attributes."""
attributes = super().state_attributes
attributes.update(
gen_state_attrs(self.component, self.component, self.active_mode)
)
return attributes


class ZoneClimate(VaillantClimate):
Expand Down Expand Up @@ -344,24 +346,33 @@ def max_temp(self):
@property
def target_temperature(self):
"""Return the temperature we try to reach."""
return self.active_mode.target_temperature
return self.active_mode.target

@property
def active_mode(self) -> ActiveMode:
"""Get active mode of the climate."""
return self._system.get_active_mode_zone(self.component)

def set_temperature(self, **kwargs):
async def async_set_temperature(self, **kwargs):
"""Set new target temperature."""
temp = kwargs.get(ATTR_TEMPERATURE)

if temp and temp != self.active_mode.target_temperature:
if temp and temp != self.active_mode.target:
_LOGGER.debug("Setting target temp to %s", temp)
self.hub.set_zone_target_temperature(self, self.component, temp)
await self.hub.set_zone_target_temperature(self, temp)
else:
_LOGGER.debug("Nothing to do")

def set_hvac_mode(self, hvac_mode):
async def async_set_hvac_mode(self, hvac_mode):
"""Set new target hvac mode."""
mode = self._HVAC_TO_MODE[hvac_mode]
self.hub.set_zone_operating_mode(self, self.component, mode)
await self.hub.set_zone_operating_mode(self, mode)

@property
def state_attributes(self) -> Dict[str, Any]:
"""Return the optional state attributes."""
attributes = super().state_attributes
attributes.update(
gen_state_attrs(self.component, self.component.heating, self.active_mode)
)
return attributes
Loading

0 comments on commit 1bb490f

Please sign in to comment.