Skip to content

Commit 9b1f985

Browse files
helto4realludeeus
andauthored
Climate support (#28)
* climate support * Add base * more services added * updated the readme * proper handling of supported_features * cleanup * more cleanups * fixed target temperature * review comments Co-authored-by: Ludeeus <[email protected]>
1 parent fb14c7d commit 9b1f985

File tree

4 files changed

+226
-0
lines changed

4 files changed

+226
-0
lines changed

INFO.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,3 +15,4 @@ For now only the folowing platforms are supported:
1515
- `binary_sensor`
1616
- `sensor`
1717
- `switch`
18+
- `climate`

README.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@ For now only the folowing platforms are supported:
1515
- `binary_sensor`
1616
- `sensor`
1717
- `switch`
18+
- `climate`
1819

1920
## Installation
2021

Lines changed: 217 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,217 @@
1+
"""Climate platform for NetDaemon."""
2+
from typing import TYPE_CHECKING
3+
4+
from homeassistant.components.climate import (
5+
ClimateEntity,
6+
ATTR_HVAC_MODE,
7+
ATTR_HVAC_MODES,
8+
ATTR_FAN_MODE,
9+
ATTR_FAN_MODES,
10+
ATTR_CURRENT_TEMPERATURE,
11+
ATTR_TEMPERATURE,
12+
)
13+
from homeassistant.components.climate.const import ATTR_HUMIDITY
14+
from homeassistant.const import ATTR_SUPPORTED_FEATURES
15+
16+
from .const import (
17+
ATTR_ATTRIBUTES,
18+
ATTR_CLIENT,
19+
ATTR_COORDINATOR,
20+
ATTR_ENTITY_ID,
21+
ATTR_STATE,
22+
DOMAIN,
23+
TEMP_CELSIUS,
24+
LOGGER,
25+
PLATFORM_CLIMATE,
26+
ATTR_TEMPERATURE_UNIT,
27+
)
28+
from .entity import NetDaemonEntity
29+
30+
if TYPE_CHECKING:
31+
from homeassistant.config_entries import ConfigEntry
32+
from homeassistant.core import HomeAssistant
33+
from homeassistant.helpers.update_coordinator import DataUpdateCoordinator
34+
35+
from .client import NetDaemonClient
36+
37+
38+
async def async_setup_entry(
39+
hass: "HomeAssistant", _config_entry: "ConfigEntry", async_add_devices
40+
) -> None:
41+
"""Setup switch platform."""
42+
client: "NetDaemonClient" = hass.data[DOMAIN][ATTR_CLIENT]
43+
coordinator: "DataUpdateCoordinator" = hass.data[DOMAIN][ATTR_COORDINATOR]
44+
45+
climate_entities = []
46+
for entity in client.entities:
47+
if entity.split(".")[0] == PLATFORM_CLIMATE:
48+
LOGGER.debug("Adding %s", entity)
49+
climate_entities.append(
50+
NetDaemonClimateEntity(coordinator, entity.split(".")[1])
51+
)
52+
53+
if climate_entities:
54+
async_add_devices(climate_entities)
55+
56+
57+
class NetDaemonClimateEntity(NetDaemonEntity, ClimateEntity):
58+
"""NetDaemon ClimateEntity class."""
59+
60+
@property
61+
def supported_features(self) -> int:
62+
"""Return the list of supported features."""
63+
if not self.entity_id:
64+
return 0
65+
return (
66+
self._coordinator.data[self.entity_id]
67+
.get(ATTR_ATTRIBUTES, {})
68+
.get(ATTR_SUPPORTED_FEATURES, 0)
69+
)
70+
71+
@property
72+
def temperature_unit(self) -> str:
73+
"""Return the unit of measurement used by the platform."""
74+
if not self.entity_id:
75+
return TEMP_CELSIUS
76+
return (
77+
self._coordinator.data[self.entity_id]
78+
.get(ATTR_ATTRIBUTES, {})
79+
.get(ATTR_TEMPERATURE_UNIT, TEMP_CELSIUS)
80+
)
81+
82+
@property
83+
def hvac_mode(self) -> str:
84+
"""Return hvac operation ie. heat, cool mode.
85+
86+
Need to be one of HVAC_MODE_*.
87+
"""
88+
if not self.entity_id:
89+
return "off"
90+
return self._coordinator.data[self.entity_id].get(ATTR_STATE, "off")
91+
92+
@property
93+
def current_temperature(self) -> float:
94+
"""Return the current temperature."""
95+
if not self.entity_id:
96+
return 0.0
97+
return (
98+
self._coordinator.data[self.entity_id]
99+
.get(ATTR_ATTRIBUTES, {})
100+
.get(ATTR_CURRENT_TEMPERATURE, 0.0)
101+
)
102+
103+
@property
104+
def target_temperature(self) -> float:
105+
"""Return the temperature we try to reach."""
106+
if not self.entity_id:
107+
return 0.0
108+
return (
109+
self._coordinator.data[self.entity_id]
110+
.get(ATTR_ATTRIBUTES, {})
111+
.get(ATTR_TEMPERATURE, 0.0)
112+
)
113+
114+
@property
115+
def target_humidity(self) -> int:
116+
"""Return the humidity we try to reach."""
117+
if not self.entity_id:
118+
return 0
119+
return (
120+
self._coordinator.data[self.entity_id]
121+
.get(ATTR_ATTRIBUTES, {})
122+
.get(ATTR_HUMIDITY, 0)
123+
)
124+
125+
@property
126+
def hvac_modes(self) -> list[str]:
127+
"""Return the list of available hvac operation modes.
128+
129+
Need to be a subset of HVAC_MODES.
130+
"""
131+
if not self.entity_id:
132+
return ["off"]
133+
134+
return (
135+
self._coordinator.data[self.entity_id]
136+
.get(ATTR_ATTRIBUTES, {})
137+
.get(ATTR_HVAC_MODES, ["off"])
138+
)
139+
140+
@property
141+
def fan_modes(self) -> list[str]:
142+
"""Return the list of available fan modes.
143+
144+
Requires SUPPORT_FAN_MODE.
145+
"""
146+
if not self.entity_id:
147+
return ["off"]
148+
149+
return (
150+
self._coordinator.data[self.entity_id]
151+
.get(ATTR_ATTRIBUTES, {})
152+
.get(ATTR_FAN_MODES, ["off"])
153+
)
154+
155+
async def async_set_temperature(self, **kwargs) -> None:
156+
"""Set new target temperature."""
157+
if not self.entity_id:
158+
LOGGER.error("Setting target temperature on non existing climate entity")
159+
return
160+
temperature = kwargs.get(ATTR_TEMPERATURE)
161+
attributes = self._coordinator.data[self.entity_id][ATTR_ATTRIBUTES]
162+
attributes[ATTR_TEMPERATURE] = temperature
163+
await self.hass.data[DOMAIN][ATTR_CLIENT].entity_update(
164+
{
165+
ATTR_ENTITY_ID: self.entity_id,
166+
ATTR_ATTRIBUTES: attributes,
167+
}
168+
)
169+
self.async_write_ha_state()
170+
171+
async def async_set_humidity(self, humidity: int) -> None:
172+
"""Set new target humidity."""
173+
if not self.entity_id:
174+
LOGGER.error("Setting target humidity on non existing climate entity")
175+
return
176+
attributes = self._coordinator.data[self.entity_id][ATTR_ATTRIBUTES]
177+
attributes[ATTR_HUMIDITY] = humidity
178+
await self.hass.data[DOMAIN][ATTR_CLIENT].entity_update(
179+
{
180+
ATTR_ENTITY_ID: self.entity_id,
181+
ATTR_ATTRIBUTES: attributes,
182+
}
183+
)
184+
self.async_write_ha_state()
185+
186+
async def async_set_fan_mode(self, fan_mode: str) -> None:
187+
"""Set new target fan mode."""
188+
if not self.entity_id:
189+
LOGGER.error("Setting target humidity on non existing climate entity")
190+
return
191+
attributes = self._coordinator.data[self.entity_id][ATTR_ATTRIBUTES]
192+
attributes[ATTR_FAN_MODE] = fan_mode
193+
await self.hass.data[DOMAIN][ATTR_CLIENT].entity_update(
194+
{
195+
ATTR_ENTITY_ID: self.entity_id,
196+
ATTR_ATTRIBUTES: attributes,
197+
}
198+
)
199+
self.async_write_ha_state()
200+
201+
async def async_set_hvac_mode(self, hvac_mode: str) -> None:
202+
"""Set new target hvac mode."""
203+
if not self.entity_id:
204+
LOGGER.error("Setting hvac mode on non existing climate entity")
205+
return
206+
207+
attributes = self._coordinator.data[self.entity_id][ATTR_ATTRIBUTES]
208+
attributes[ATTR_HVAC_MODE] = hvac_mode
209+
LOGGER.debug("Set hwac_mode %s %s", self.entity_id, hvac_mode)
210+
await self.hass.data[DOMAIN][ATTR_CLIENT].entity_update(
211+
{
212+
ATTR_ENTITY_ID: self.entity_id,
213+
ATTR_STATE: hvac_mode,
214+
ATTR_ATTRIBUTES: attributes,
215+
}
216+
)
217+
self.async_write_ha_state()

custom_components/netdaemon/const.py

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,10 +17,15 @@
1717
ATTR_METHOD = "method"
1818
ATTR_ENTITY_ID = "entity_id"
1919
ATTR_STATE = "state"
20+
ATTR_HVAC_MODE = "hvac_mode"
2021
ATTR_ICON = "icon"
2122
ATTR_UNIT = "unit"
2223
ATTR_ATTRIBUTES = "attributes"
2324
ATTR_VERSION = "version"
25+
ATTR_TARGET_TEMPERATURE = "target_temperature"
26+
ATTR_TARGET_HUMIDITY = "target_humidity"
27+
ATTR_TEMPERATURE_UNIT = "temperature_unit"
28+
TEMP_CELSIUS = "°C"
2429

2530
REASON_MIN_HA_VERSION = "min_ha_version"
2631
REASON_SINGLE = "single_instance_allowed"
@@ -52,11 +57,13 @@
5257
PLATFORM_BINARY_SENSOR = "binary_sensor"
5358
PLATFORM_SENSOR = "sensor"
5459
PLATFORM_SWITCH = "switch"
60+
PLATFORM_CLIMATE = "climate"
5561

5662
PLATFORMS = [
5763
PLATFORM_BINARY_SENSOR,
5864
PLATFORM_SENSOR,
5965
PLATFORM_SWITCH,
66+
PLATFORM_CLIMATE,
6067
]
6168

6269
STATE_ON_VALUES = set(["true", "on", "1"])

0 commit comments

Comments
 (0)