Skip to content

Commit 94867d9

Browse files
author
Johan du Plessis
committed
Initial Victron Venus implementation.
1 parent 5cce369 commit 94867d9

24 files changed

+1876
-0
lines changed

CODEOWNERS

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1611,6 +1611,8 @@ build.json @home-assistant/supervisor
16111611
/tests/components/vesync/ @markperdue @webdjoe @thegardenmonkey @cdnninja
16121612
/homeassistant/components/vicare/ @CFenner
16131613
/tests/components/vicare/ @CFenner
1614+
/homeassistant/components/victronvenus/ @johanslab
1615+
/tests/components/victronvenus/ @johanslab
16141616
/homeassistant/components/vilfo/ @ManneW
16151617
/tests/components/vilfo/ @ManneW
16161618
/homeassistant/components/vivotek/ @HarlemSquirrel
Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,33 @@
1+
"""The victronvenus integration."""
2+
3+
from __future__ import annotations
4+
5+
import logging
6+
7+
from homeassistant.const import Platform
8+
from homeassistant.core import HomeAssistant
9+
10+
from ._hubcontrol import _async_setup_hub, _async_unload_hub
11+
from .const import DOMAIN
12+
from .victronvenus_base import VictronVenusConfigEntry as _VictronVenusConfigEntry
13+
14+
_LOGGER = logging.getLogger(__name__)
15+
16+
PLATFORMS: list[Platform] = [Platform.SENSOR]
17+
18+
__all__ = ["async_setup_entry", "async_unload_entry", "DOMAIN"]
19+
20+
21+
async def async_setup_entry(
22+
hass: HomeAssistant, entry: _VictronVenusConfigEntry
23+
) -> bool:
24+
"""Set up victronvenus from a config entry."""
25+
return await _async_setup_hub(hass, entry, PLATFORMS)
26+
27+
28+
async def async_unload_entry(
29+
hass: HomeAssistant, entry: _VictronVenusConfigEntry
30+
) -> bool:
31+
"""Unload a config entry."""
32+
33+
return await _async_unload_hub(hass, entry, PLATFORMS)
Lines changed: 58 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,58 @@
1+
from __future__ import annotations
2+
3+
from homeassistant.const import (
4+
CONF_HOST,
5+
CONF_PASSWORD,
6+
CONF_PORT,
7+
CONF_SSL,
8+
CONF_USERNAME,
9+
Platform,
10+
)
11+
from homeassistant.core import HomeAssistant
12+
13+
from .const import CONF_INSTALLATION_ID, CONF_MODEL, CONF_SERIAL
14+
from .victronvenus_base import VictronVenusConfigEntry
15+
from .victronvenus_hub import VictronVenusHub
16+
17+
18+
async def _async_setup_hub(
19+
hass: HomeAssistant, entry: VictronVenusConfigEntry, platforms: list[Platform]
20+
) -> bool:
21+
"""Set up victronvenus from a config entry."""
22+
23+
config = entry.data
24+
hub = VictronVenusHub(
25+
hass,
26+
config.get(CONF_HOST),
27+
config.get(CONF_PORT, 1883),
28+
config.get(CONF_USERNAME),
29+
config.get(CONF_PASSWORD),
30+
config.get(CONF_SERIAL),
31+
config.get(CONF_SSL, False),
32+
config.get(CONF_INSTALLATION_ID),
33+
config.get(CONF_MODEL),
34+
)
35+
36+
await hub.connect()
37+
38+
await hub.setup_subscriptions()
39+
await hub.initiate_keep_alive()
40+
await hub.wait_for_first_refresh()
41+
42+
entry.runtime_data = hub
43+
44+
await hass.config_entries.async_forward_entry_setups(entry, platforms)
45+
46+
return True
47+
48+
49+
async def _async_unload_hub(
50+
hass: HomeAssistant, entry: VictronVenusConfigEntry, platforms: list[Platform]
51+
) -> bool:
52+
"""Unload a config entry."""
53+
54+
hub = entry.runtime_data
55+
if hub is not None:
56+
if isinstance(hub, VictronVenusHub):
57+
await hub.disconnect()
58+
return await hass.config_entries.async_unload_platforms(entry, platforms)
Lines changed: 277 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,277 @@
1+
from homeassistant.components.sensor.const import SensorDeviceClass, SensorStateClass
2+
3+
from ._unwrappers import unwrap_float, unwrap_int, unwrap_int_default_0, unwrap_string
4+
from .const import DEVICE_MESSAGE, SENSOR_MESSAGE
5+
from .data_classes import TopicDescriptor
6+
7+
topic_map = {
8+
# generic device attributes
9+
"N/+/+/+/ProductName": TopicDescriptor(
10+
message_type=DEVICE_MESSAGE,
11+
short_id="model",
12+
unit_of_measurement=None,
13+
device_class=None,
14+
state_class=None,
15+
precision=None,
16+
unwrapper=unwrap_string,
17+
),
18+
"N/+/+/+/Serial": TopicDescriptor(
19+
message_type=DEVICE_MESSAGE,
20+
short_id="serial_number",
21+
unit_of_measurement=None,
22+
device_class=None,
23+
state_class=None,
24+
precision=None,
25+
unwrapper=unwrap_string,
26+
),
27+
"N/+/+/+/Manufacturer": TopicDescriptor(
28+
message_type=DEVICE_MESSAGE,
29+
short_id="manufacturer",
30+
unit_of_measurement=None,
31+
device_class=None,
32+
state_class=None,
33+
precision=None,
34+
unwrapper=unwrap_string,
35+
),
36+
"N/+/+/+/ProductId": TopicDescriptor(
37+
message_type=DEVICE_MESSAGE,
38+
short_id="victron_productid",
39+
unit_of_measurement=None,
40+
device_class=None,
41+
state_class=None,
42+
precision=0,
43+
unwrapper=unwrap_int,
44+
),
45+
# grid
46+
"N/+/system/+/Ac/Grid/NumberOfPhases": TopicDescriptor(
47+
message_type=SENSOR_MESSAGE,
48+
short_id="grid_phases",
49+
unit_of_measurement=None,
50+
device_class=None,
51+
state_class=SensorStateClass.TOTAL,
52+
precision=0,
53+
unwrapper=unwrap_int_default_0,
54+
),
55+
# individual grid phases
56+
"N/+/grid/+/Ac/+/Voltage": TopicDescriptor(
57+
message_type=SENSOR_MESSAGE,
58+
short_id="grid_voltage_{phase}",
59+
unit_of_measurement="V",
60+
device_class=SensorDeviceClass.VOLTAGE,
61+
state_class=SensorStateClass.MEASUREMENT,
62+
precision=1,
63+
unwrapper=unwrap_float,
64+
),
65+
"N/+/grid/+/Ac/+/Current": TopicDescriptor(
66+
message_type="sensor",
67+
short_id="grid_current_{phase}",
68+
unit_of_measurement="A",
69+
device_class=SensorDeviceClass.CURRENT,
70+
state_class=SensorStateClass.MEASUREMENT,
71+
precision=1,
72+
unwrapper=unwrap_float,
73+
),
74+
"N/+/grid/+/Ac/+/Power": TopicDescriptor(
75+
message_type=SENSOR_MESSAGE,
76+
short_id="grid_power_{phase}",
77+
unit_of_measurement="W",
78+
device_class=SensorDeviceClass.POWER,
79+
state_class=SensorStateClass.MEASUREMENT,
80+
precision=1,
81+
unwrapper=unwrap_float,
82+
),
83+
# total grid
84+
"N/+/grid/+/Ac/Voltage": TopicDescriptor(
85+
message_type=SENSOR_MESSAGE,
86+
short_id="grid_voltage",
87+
unit_of_measurement="V",
88+
device_class=SensorDeviceClass.VOLTAGE,
89+
state_class=SensorStateClass.MEASUREMENT,
90+
precision=1,
91+
unwrapper=unwrap_float,
92+
),
93+
"N/+/grid/+/Ac/Current": TopicDescriptor(
94+
message_type=SENSOR_MESSAGE,
95+
short_id="grid_current",
96+
unit_of_measurement="A",
97+
device_class=SensorDeviceClass.CURRENT,
98+
state_class=SensorStateClass.MEASUREMENT,
99+
precision=1,
100+
unwrapper=unwrap_float,
101+
),
102+
"N/+/grid/+/Ac/Power": TopicDescriptor(
103+
message_type=SENSOR_MESSAGE,
104+
short_id="grid_power",
105+
unit_of_measurement="W",
106+
device_class=SensorDeviceClass.POWER,
107+
state_class=SensorStateClass.MEASUREMENT,
108+
precision=1,
109+
unwrapper=unwrap_float,
110+
),
111+
"N/+/grid/+/Ac/Energy/Forward": TopicDescriptor(
112+
message_type=SENSOR_MESSAGE,
113+
short_id="grid_energy_forward",
114+
unit_of_measurement="kWh",
115+
device_class=SensorDeviceClass.ENERGY,
116+
state_class=SensorStateClass.TOTAL_INCREASING,
117+
precision=1,
118+
unwrapper=unwrap_float,
119+
),
120+
"N/+/grid/+/Ac/Energy/Reverse": TopicDescriptor(
121+
message_type=SENSOR_MESSAGE,
122+
short_id="grid_energy_reverse",
123+
unit_of_measurement="kWh",
124+
device_class=SensorDeviceClass.ENERGY,
125+
state_class=SensorStateClass.TOTAL_INCREASING,
126+
precision=1,
127+
unwrapper=unwrap_float,
128+
),
129+
# solar / MPPT
130+
"N/+/solarcharger/+/Dc/0/Voltage": TopicDescriptor(
131+
message_type=SENSOR_MESSAGE,
132+
short_id="solar_voltage",
133+
unit_of_measurement="V",
134+
device_class=SensorDeviceClass.VOLTAGE,
135+
state_class=SensorStateClass.MEASUREMENT,
136+
precision=1,
137+
unwrapper=unwrap_float,
138+
),
139+
"N/+/solarcharger/+/Dc/0/Current": TopicDescriptor(
140+
message_type=SENSOR_MESSAGE,
141+
short_id="solar_current",
142+
unit_of_measurement="A",
143+
device_class=SensorDeviceClass.CURRENT,
144+
state_class=SensorStateClass.MEASUREMENT,
145+
precision=1,
146+
unwrapper=unwrap_float,
147+
),
148+
"N/+/solarcharger/+/Yield/Power": TopicDescriptor(
149+
message_type=SENSOR_MESSAGE,
150+
short_id="solar_power",
151+
unit_of_measurement="W",
152+
device_class=SensorDeviceClass.POWER,
153+
state_class=SensorStateClass.MEASUREMENT,
154+
precision=1,
155+
unwrapper=unwrap_float,
156+
),
157+
"N/+/solarcharger/+/Yield/User": TopicDescriptor(
158+
message_type=SENSOR_MESSAGE,
159+
short_id="solar_yield",
160+
unit_of_measurement="kWh",
161+
device_class=SensorDeviceClass.ENERGY,
162+
state_class=SensorStateClass.TOTAL_INCREASING,
163+
precision=1,
164+
unwrapper=unwrap_float,
165+
),
166+
"N/+/solarcharger/+/History/Daily/0/MaxPower": TopicDescriptor(
167+
message_type=SENSOR_MESSAGE,
168+
short_id="solar_max_power",
169+
unit_of_measurement="W",
170+
device_class=SensorDeviceClass.POWER,
171+
state_class=SensorStateClass.MEASUREMENT,
172+
precision=1,
173+
unwrapper=unwrap_float,
174+
),
175+
# batteries
176+
"N/+/battery/+/Dc/0/Voltage": TopicDescriptor(
177+
message_type=SENSOR_MESSAGE,
178+
short_id="battery_voltage",
179+
unit_of_measurement="V",
180+
device_class=SensorDeviceClass.VOLTAGE,
181+
state_class=SensorStateClass.MEASUREMENT,
182+
precision=1,
183+
unwrapper=unwrap_float,
184+
),
185+
"N/+/battery/+/Dc/0/Current": TopicDescriptor(
186+
message_type=SENSOR_MESSAGE,
187+
short_id="battery_current",
188+
unit_of_measurement="A",
189+
device_class=SensorDeviceClass.CURRENT,
190+
state_class=SensorStateClass.MEASUREMENT,
191+
precision=1,
192+
unwrapper=unwrap_float,
193+
),
194+
"N/+/battery/+/Dc/0/Power": TopicDescriptor(
195+
message_type=SENSOR_MESSAGE,
196+
short_id="battery_power",
197+
unit_of_measurement="W",
198+
device_class=SensorDeviceClass.POWER,
199+
state_class=SensorStateClass.MEASUREMENT,
200+
precision=1,
201+
unwrapper=unwrap_float,
202+
),
203+
"N/+/battery/+/Dc/0/Temperature": TopicDescriptor(
204+
message_type=SENSOR_MESSAGE,
205+
short_id="battery_temperature",
206+
unit_of_measurement="°C",
207+
device_class=SensorDeviceClass.TEMPERATURE,
208+
state_class=SensorStateClass.MEASUREMENT,
209+
precision=1,
210+
unwrapper=unwrap_float,
211+
),
212+
"N/+/battery/+/History/DischargedEnergy": TopicDescriptor(
213+
message_type=SENSOR_MESSAGE,
214+
short_id="battery_discharged_energy",
215+
unit_of_measurement="kWh",
216+
device_class=SensorDeviceClass.ENERGY,
217+
state_class=SensorStateClass.TOTAL_INCREASING,
218+
precision=1,
219+
unwrapper=unwrap_float,
220+
),
221+
"N/+/battery/+/History/ChargedEnergy": TopicDescriptor(
222+
message_type=SENSOR_MESSAGE,
223+
short_id="battery_charged_energy",
224+
unit_of_measurement="kWh",
225+
device_class=SensorDeviceClass.ENERGY,
226+
state_class=SensorStateClass.TOTAL_INCREASING,
227+
precision=1,
228+
unwrapper=unwrap_float,
229+
),
230+
"N/+/battery/+/Capacity": TopicDescriptor(
231+
message_type=SENSOR_MESSAGE,
232+
short_id="battery_capacity",
233+
unit_of_measurement="Ah",
234+
device_class=None,
235+
state_class=SensorStateClass.MEASUREMENT,
236+
precision=1,
237+
unwrapper=unwrap_float,
238+
),
239+
"N/+/battery/+/InstalledCapacity": TopicDescriptor(
240+
message_type=SENSOR_MESSAGE,
241+
short_id="battery_installed_capacity",
242+
unit_of_measurement="Ah",
243+
device_class=None,
244+
state_class=SensorStateClass.MEASUREMENT,
245+
precision=1,
246+
unwrapper=unwrap_float,
247+
),
248+
"N/+/battery/+/Soc": TopicDescriptor(
249+
message_type=SENSOR_MESSAGE,
250+
short_id="battery_soc",
251+
unit_of_measurement="%",
252+
device_class=SensorDeviceClass.BATTERY,
253+
state_class=SensorStateClass.MEASUREMENT,
254+
precision=1,
255+
unwrapper=unwrap_float,
256+
),
257+
# integrated system. Note that this might not be currently accurate for all systems as there are different physical configurations
258+
# and don't have access to any other for testing or feedback.
259+
"N/+/system/+/Ac/ConsumptionOnOutput/+/Power": TopicDescriptor(
260+
message_type=SENSOR_MESSAGE,
261+
short_id="critical_loads_{phase}",
262+
unit_of_measurement="W",
263+
device_class=SensorDeviceClass.POWER,
264+
state_class=SensorStateClass.MEASUREMENT,
265+
precision=1,
266+
unwrapper=unwrap_float,
267+
),
268+
"N/+/system/+/Ac/ConsumptionOnInput/+/Power": TopicDescriptor(
269+
message_type=SENSOR_MESSAGE,
270+
short_id="ac_loads_{phase}",
271+
unit_of_measurement="W",
272+
device_class=SensorDeviceClass.POWER,
273+
state_class=SensorStateClass.MEASUREMENT,
274+
precision=1,
275+
unwrapper=unwrap_float,
276+
),
277+
}

0 commit comments

Comments
 (0)