-
-
Notifications
You must be signed in to change notification settings - Fork 37.4k
Add Wibeee energy monitoring integration #168419
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: dev
Are you sure you want to change the base?
Changes from 81 commits
e89eee4
c633461
11a0a06
500e050
b72296d
683d672
82a062e
20ad7b9
d190c8a
09ed254
e02601a
ae975a7
6133c75
a8edf92
14a448b
211d9c9
042660c
e1d7e99
d12073a
570c6fd
6b00186
5323c3d
8b88cc1
c1eaa5e
8c4d140
9d74d4b
beb4d58
61e6eec
3eeb385
f3194dd
cfc02ff
58bc6df
5995efe
a0b36f2
13abc52
00dcc3d
687b01f
effed37
70e30a2
65be1d3
e935d73
ed378f8
cc16f05
32862e1
88e883e
12644ca
1dd0bef
0cd4457
608fa8f
63c2b26
0872a3e
b7aa02a
ba45f83
adf39b7
1ff85ba
65b4280
bc48239
d54e23b
62d3a56
209df15
dc21e5f
4b6ef46
d69385d
7c4401a
d6282af
bf13e93
689aeca
0bddaf6
ecbb3af
6d560b0
c3afef3
73f6638
2609965
5aae960
14e3db7
9f2caee
ed20679
e269140
9613bbf
24f1e87
c1b443e
db262df
e381e63
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.
| Original file line number | Diff line number | Diff line change | ||||||||||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| @@ -0,0 +1,161 @@ | ||||||||||||||||||||
| """The Wibeee integration.""" | ||||||||||||||||||||
|
|
||||||||||||||||||||
| from __future__ import annotations | ||||||||||||||||||||
|
|
||||||||||||||||||||
| from dataclasses import dataclass | ||||||||||||||||||||
| import ipaddress | ||||||||||||||||||||
| import logging | ||||||||||||||||||||
| import socket | ||||||||||||||||||||
| from xml.etree.ElementTree import ParseError as XMLParseError | ||||||||||||||||||||
|
|
||||||||||||||||||||
| import aiohttp | ||||||||||||||||||||
| from pywibeee import WibeeeAPI, WibeeeDeviceInfo | ||||||||||||||||||||
|
|
||||||||||||||||||||
| from homeassistant.config_entries import ConfigEntry | ||||||||||||||||||||
| from homeassistant.const import CONF_HOST, Platform | ||||||||||||||||||||
| from homeassistant.core import HomeAssistant | ||||||||||||||||||||
| from homeassistant.exceptions import ConfigEntryNotReady | ||||||||||||||||||||
| from homeassistant.helpers.aiohttp_client import async_get_clientsession | ||||||||||||||||||||
|
|
||||||||||||||||||||
| from .const import ( | ||||||||||||||||||||
| CONF_MAC_ADDRESS, | ||||||||||||||||||||
| CONF_UPDATE_MODE, | ||||||||||||||||||||
| CONF_WIBEEE_ID, | ||||||||||||||||||||
| DEFAULT_SCAN_INTERVAL, | ||||||||||||||||||||
| MODE_LOCAL_PUSH, | ||||||||||||||||||||
| MODE_POLLING, | ||||||||||||||||||||
| PUSH_STALE_AFTER, | ||||||||||||||||||||
| ) | ||||||||||||||||||||
| from .coordinator import WibeeeCoordinator | ||||||||||||||||||||
| from .push_receiver import async_setup_push_receiver | ||||||||||||||||||||
|
|
||||||||||||||||||||
| _LOGGER = logging.getLogger(__name__) | ||||||||||||||||||||
|
|
||||||||||||||||||||
| PLATFORMS = [Platform.SENSOR] | ||||||||||||||||||||
|
|
||||||||||||||||||||
|
Comment on lines
+34
to
+35
|
||||||||||||||||||||
|
|
||||||||||||||||||||
| @dataclass | ||||||||||||||||||||
| class WibeeeRuntimeData: | ||||||||||||||||||||
| """Runtime data stored in entry.runtime_data.""" | ||||||||||||||||||||
|
|
||||||||||||||||||||
| api: WibeeeAPI | ||||||||||||||||||||
| device_info: WibeeeDeviceInfo | ||||||||||||||||||||
| coordinator: WibeeeCoordinator | ||||||||||||||||||||
|
|
||||||||||||||||||||
|
|
||||||||||||||||||||
| WibeeeConfigEntry = ConfigEntry[WibeeeRuntimeData] | ||||||||||||||||||||
|
|
||||||||||||||||||||
|
|
||||||||||||||||||||
| async def async_setup_entry(hass: HomeAssistant, entry: WibeeeConfigEntry) -> bool: | ||||||||||||||||||||
| """Set up Wibeee from a config entry.""" | ||||||||||||||||||||
| mode = entry.options.get(CONF_UPDATE_MODE, MODE_LOCAL_PUSH) | ||||||||||||||||||||
| host = entry.data[CONF_HOST] | ||||||||||||||||||||
| mac_addr = entry.data[CONF_MAC_ADDRESS] | ||||||||||||||||||||
| wibeee_id = entry.data.get(CONF_WIBEEE_ID, "WIBEEE") | ||||||||||||||||||||
|
|
||||||||||||||||||||
| _LOGGER.debug( | ||||||||||||||||||||
| "Setting up Wibeee entry %s (mode=%s, host=%s)", | ||||||||||||||||||||
| entry.entry_id, | ||||||||||||||||||||
| mode, | ||||||||||||||||||||
| host, | ||||||||||||||||||||
| ) | ||||||||||||||||||||
|
|
||||||||||||||||||||
| session = async_get_clientsession(hass) | ||||||||||||||||||||
| api = WibeeeAPI(session, host) | ||||||||||||||||||||
|
|
||||||||||||||||||||
| # Fetch device info | ||||||||||||||||||||
| try: | ||||||||||||||||||||
| device_info = await api.async_fetch_device_info(retries=3) | ||||||||||||||||||||
| except (TimeoutError, aiohttp.ClientError) as err: | ||||||||||||||||||||
| raise ConfigEntryNotReady(f"Could not connect to Wibeee at {host}") from err | ||||||||||||||||||||
|
|
||||||||||||||||||||
| if device_info is None: | ||||||||||||||||||||
| _LOGGER.warning("Could not get device info from %s, using fallback", host) | ||||||||||||||||||||
| device_info = WibeeeDeviceInfo( | ||||||||||||||||||||
| wibeee_id=wibeee_id, | ||||||||||||||||||||
| mac_addr=mac_addr, | ||||||||||||||||||||
| model="Unknown", | ||||||||||||||||||||
| firmware_version="Unknown", | ||||||||||||||||||||
| ip_addr=host, | ||||||||||||||||||||
| ) | ||||||||||||||||||||
|
|
||||||||||||||||||||
| # Create coordinator based on mode | ||||||||||||||||||||
| if mode == MODE_POLLING: | ||||||||||||||||||||
| coordinator = WibeeeCoordinator( | ||||||||||||||||||||
| hass, | ||||||||||||||||||||
| api, | ||||||||||||||||||||
| config_entry=entry, | ||||||||||||||||||||
| name=f"Wibeee {device_info.mac_addr_short}", | ||||||||||||||||||||
|
Comment on lines
+83
to
+88
|
||||||||||||||||||||
| update_interval=DEFAULT_SCAN_INTERVAL, | ||||||||||||||||||||
| ) | ||||||||||||||||||||
| await coordinator.async_config_entry_first_refresh() | ||||||||||||||||||||
|
Comment on lines
+84
to
+91
|
||||||||||||||||||||
| else: | ||||||||||||||||||||
| # Push mode: no polling, data arrives via async_set_updated_data() | ||||||||||||||||||||
| coordinator = WibeeeCoordinator( | ||||||||||||||||||||
| hass, | ||||||||||||||||||||
| api, | ||||||||||||||||||||
| config_entry=entry, | ||||||||||||||||||||
| name=f"Wibeee {device_info.mac_addr_short}", | ||||||||||||||||||||
| update_interval=None, | ||||||||||||||||||||
|
Comment on lines
+94
to
+99
|
||||||||||||||||||||
| stale_after=PUSH_STALE_AFTER, | ||||||||||||||||||||
| ) | ||||||||||||||||||||
| # Do one initial poll to discover available sensors | ||||||||||||||||||||
| try: | ||||||||||||||||||||
| initial_data = await api.async_fetch_sensors_data(retries=3) | ||||||||||||||||||||
| except (TimeoutError, aiohttp.ClientError, XMLParseError) as err: | ||||||||||||||||||||
| raise ConfigEntryNotReady(f"Error connecting to Wibeee at {host}") from err | ||||||||||||||||||||
|
Comment on lines
+102
to
+106
|
||||||||||||||||||||
|
|
||||||||||||||||||||
| if not initial_data or not isinstance(initial_data, dict): | ||||||||||||||||||||
| raise ConfigEntryNotReady( | ||||||||||||||||||||
| f"Could not fetch initial sensor data from Wibeee at {host}" | ||||||||||||||||||||
|
Comment on lines
+102
to
+110
|
||||||||||||||||||||
| ) | ||||||||||||||||||||
|
Comment on lines
+102
to
+111
|
||||||||||||||||||||
|
|
||||||||||||||||||||
|
Comment on lines
+102
to
+112
|
||||||||||||||||||||
| coordinator.async_set_updated_data(initial_data) |
Copilot
AI
Apr 28, 2026
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Validate that the initial sensor payload fetched in push mode is a dict before storing it in the coordinator to avoid runtime errors in sensor entities when the library returns an unexpected type.
| if not isinstance(initial_data, dict): | |
| raise ConfigEntryNotReady( | |
| f"Invalid initial sensor data received from Wibeee at {host}" | |
| ) |
Copilot
AI
Apr 24, 2026
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Register the push receiver with a concrete device IP (or resolve hostnames) so IP validation doesn’t reject legitimate push requests when the user configured the device via hostname.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Align the PR description with the implementation (or add the missing platform): the integration currently only forwards the sensor platform (no button platform/entities are set up).