Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion .gitignore
Original file line number Diff line number Diff line change
@@ -1,2 +1,2 @@
/.idea/
/__pycache__/
__pycache__/
1 change: 1 addition & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@ You have two options for installation:
- Restart HA server.

### WORKING ON:
- Brink Flair 200
- Brink Renovent 180
- Brink Renovent 300
- Brink Renovent 400 Plus
Expand Down
8 changes: 7 additions & 1 deletion custom_components/brink_ventilation/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@

_LOGGER = logging.getLogger(__name__)

PLATFORMS = [Platform.SELECT, Platform.BINARY_SENSOR, Platform.FAN]
PLATFORMS = [Platform.SELECT, Platform.BINARY_SENSOR, Platform.FAN, Platform.SENSOR]

CONFIG_SCHEMA = cv.removed(DOMAIN, raise_if_present=False)

Expand Down Expand Up @@ -100,9 +100,15 @@ async def async_get_devices(hass: HomeAssistant, entry: ConfigEntry, brink_clien
# Retrieve additional description
for system in systems:
description = await brink_client.get_description_values(system["system_id"], system["gateway_id"])

# Add core ventilation control values
system["ventilation"] = description["ventilation"]
system["mode"] = description["mode"]
system["filters_need_change"] = description["filters_need_change"]

# Add any additional sensors (CO2, temperature, humidity, etc.)
for key, value in description.items():
system[key] = value

hass.data[DOMAIN][entry.entry_id][DATA_DEVICES] = systems

Expand Down
2 changes: 1 addition & 1 deletion custom_components/brink_ventilation/binary_sensor.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@
from homeassistant.config_entries import ConfigEntry
from homeassistant.core import HomeAssistant

from custom_components.brink_ventilation import BrinkHomeDeviceEntity
from . import BrinkHomeDeviceEntity

from .const import (
DATA_CLIENT,
Expand Down
27 changes: 27 additions & 0 deletions custom_components/brink_ventilation/const.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,6 @@
"""Constant values for the Brink Home component."""
from homeassistant.components.sensor import SensorDeviceClass, SensorStateClass
from homeassistant.const import CONCENTRATION_PARTS_PER_MILLION, UnitOfTime

DOMAIN = "brink_ventilation"
DEFAULT_NAME = "Brink"
Expand All @@ -11,3 +13,28 @@
DEFAULT_SCAN_INTERVAL = 30

API_URL = "https://www.brink-home.com/portal/api/portal/"

# Define sensor types with their properties
SENSOR_TYPES = {
"co2": {
"device_class": SensorDeviceClass.CO2,
"state_class": SensorStateClass.MEASUREMENT,
"unit": CONCENTRATION_PARTS_PER_MILLION,
"icon": "mdi:molecule-co2",
"pattern": r"(?=.*\bPPM\b)(?=.*\bCO2\b)",
},
"bypass": {
"device_class": None,
"state_class": None,
"unit": None,
"icon": "mdi:valve",
"pattern": r"Status Bypassklappe",
},
"mode_timer": {
"device_class": SensorDeviceClass.DURATION,
"state_class": None,
"unit": UnitOfTime.MINUTES,
"icon": "mdi:timer-outline",
"pattern": r"Restlaufzeit Betriebsartfunktion",
},
}
95 changes: 66 additions & 29 deletions custom_components/brink_ventilation/core/brink_home_cloud.py
Original file line number Diff line number Diff line change
@@ -1,10 +1,12 @@
"""Implementation for Brink-Home Cloud"""
import asyncio
import async_timeout
import logging
import re

import aiohttp
import async_timeout

from ..const import API_URL
from ..const import API_URL, SENSOR_TYPES
from ..translations import TRANSLATIONS

_LOGGER = logging.getLogger(__name__)
Expand All @@ -31,15 +33,15 @@ async def _api_call(self, url, method, data=None):
"%s request: %s, data %s",
method,
url,
data
data,
)
try:
async with async_timeout.timeout(self.timeout):
req = await self._http_session.request(
method,
url,
json=data,
headers=self.headers
headers=self.headers,
)

if req.status == 401:
Expand Down Expand Up @@ -72,7 +74,7 @@ async def login(self):

_LOGGER.debug(
"login result: %s",
result
result,
)

return result
Expand All @@ -87,50 +89,80 @@ async def get_systems(self):
mapped_result = []

for system in result:
mapped_result.append({
'system_id': system["id"],
'gateway_id': system["gatewayId"],
'name': system['name']
})
mapped_result.append(
{
'system_id': system["id"],
'gateway_id': system["gatewayId"],
'name': system['name']
},
)

_LOGGER.debug(
"get_systems result: %s",
mapped_result
mapped_result,
)

return mapped_result

async def get_description_values(self, system_id, gateway_id):
"""Gets values info."""
url = f"{API_URL}GetAppGuiDescriptionForGateway?GatewayId={gateway_id}&SystemId={system_id}"
url = (f"{API_URL}GetAppGuiDescriptionForGateway?GatewayId="
f"{gateway_id}&SystemId={system_id}")

response = await self._api_call(url, "GET")
result = await response.json()

_LOGGER.debug(
"Response result: %s",
result
result,
)

menu_items = result.get("menuItems", [])
if not menu_items:
_LOGGER.debug("No menu items found in API response")
return {}

menu_item = menu_items[0]
pages = menu_item.get("pages", [])
home_page = pages[0]
parameters = home_page.get("parameterDescriptors", [])
ventilation = self.__find(parameters, "uiId", "Lüftungsstufe")
mode = self.__find(parameters, "uiId", "Betriebsart")
filters_need_change = self.__find(parameters, "uiId", "Status Filtermeldung")

if not pages:
_LOGGER.debug("No pages found in menu item")
return {}

# Extract all parameters from all pages
all_parameters = []
for page in pages:
parameters = page.get("parameterDescriptors", [])
all_parameters.extend(parameters)

_LOGGER.debug(f"Found {len(all_parameters)} parameters across all pages")

# Find the basic parameters
ventilation = self.__find(all_parameters, "uiId", "Lüftungsstufe")
mode = self.__find(all_parameters, "uiId", "Betriebsart")
filters_need_change = self.__find(
all_parameters, "uiId", "Status Filtermeldung",
)

# Initialize the result dictionary with the basic parameters
description_result = {
"ventilation": self.__get_type(ventilation),
"mode": self.__get_type(mode),
"filters_need_change": self.__get_type(filters_need_change)
}

# Look for all sensor types and add them to the result
for param in all_parameters:
param_name = param.get("name", "")

# Check against all defined sensor types
for sensor_type, properties in SENSOR_TYPES.items():
if re.search(properties["pattern"], param_name):
_LOGGER.debug(f"Found {sensor_type} sensor: {param_name}")
description_result[param_name] = self.__get_type(param)

_LOGGER.debug(
"get_description_values result: %s",
description_result
description_result,
)

return description_result
Expand All @@ -149,15 +181,21 @@ def __get_values(type):
extracted = []
for value in values:
if value["isSelectable"]:
extracted.append({
"value": value["value"],
"text": TRANSLATIONS.get(value["displayText"], value["displayText"])
})
extracted.append(
{
"value": value["value"],
"text": TRANSLATIONS.get(
value["displayText"], value["displayText"],
)
},
)

return extracted

# 1 as mode value changes mode to manual every time you change ventilation value
async def set_ventilation_value(self, system_id, gateway_id, mode, ventilation, value):
async def set_ventilation_value(
self, system_id, gateway_id, mode, ventilation, value,
):
ventilation_value = ventilation["values"][value]["value"]
if ventilation_value is None:
return
Expand Down Expand Up @@ -203,14 +241,13 @@ async def set_mode_value(self, system_id, gateway_id, mode, value):

await self._api_call(url, "POST", data)

def __find(self, arr , attr, value):
def __find(self, arr, attr, value):
for obj in arr:
try:
if obj[attr] == value:
return obj
except:
_LOGGER.debug(
"find error: %s",
value
value,
)

8 changes: 2 additions & 6 deletions custom_components/brink_ventilation/fan.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,16 +3,12 @@
import logging
import math

from homeassistant.components.fan import (
DOMAIN,
FanEntity,
FanEntityFeature
)
from homeassistant.components.fan import FanEntity, FanEntityFeature
from homeassistant.config_entries import ConfigEntry
from homeassistant.core import HomeAssistant
from homeassistant.util.percentage import int_states_in_range, ranged_value_to_percentage, percentage_to_ranged_value

from custom_components.brink_ventilation import BrinkHomeDeviceEntity
from . import BrinkHomeDeviceEntity

from .const import (
DATA_CLIENT,
Expand Down
2 changes: 1 addition & 1 deletion custom_components/brink_ventilation/select.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@
from homeassistant.config_entries import ConfigEntry
from homeassistant.core import HomeAssistant

from custom_components.brink_ventilation import BrinkHomeDeviceEntity
from . import BrinkHomeDeviceEntity

from .const import (
DATA_CLIENT,
Expand Down
Loading