Skip to content

Commit

Permalink
Merge pull request #49 from P-Storm/fix/multiple-devices
Browse files Browse the repository at this point in the history
Fixes multiple devices grouped weird
  • Loading branch information
koenhendriks authored Mar 24, 2024
2 parents e6b1d85 + 6bdc778 commit 826102c
Show file tree
Hide file tree
Showing 10 changed files with 195 additions and 94 deletions.
12 changes: 7 additions & 5 deletions custom_components/button_plus/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,10 +7,11 @@
from homeassistant.core import HomeAssistant
from homeassistant.helpers.device_registry import DeviceEntry

from .button_plus_api.model import DeviceConfiguration
from .buttonplushub import ButtonPlusHub
from .const import DOMAIN
from .coordinator import ButtonPlusCoordinator
from custom_components.button_plus.button_plus_api.model import DeviceConfiguration
from custom_components.button_plus.buttonplushub import ButtonPlusHub
from custom_components.button_plus.const import DOMAIN
from custom_components.button_plus.coordinator import ButtonPlusCoordinator


_LOGGER = logging.getLogger(__name__)

Expand All @@ -33,10 +34,11 @@ async def async_setup_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool:

# This creates each HA object for each platform your device requires.
# It's done by calling the `async_setup_entry` function in each platform module.


await hass.config_entries.async_forward_entry_setups(entry, PLATFORMS)
return True


async def async_unload_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool:
"""Unload a config entry."""
# This is called when an entry/configured device is to be removed. The class
Expand Down
43 changes: 19 additions & 24 deletions custom_components/button_plus/button.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,8 +7,9 @@
from homeassistant.config_entries import ConfigEntry
from homeassistant.core import HomeAssistant
from homeassistant.helpers.entity_platform import AddEntitiesCallback
from homeassistant.helpers.device_registry import DeviceInfo

from custom_components.button_plus.button_plus_api.model import Connector
from custom_components.button_plus.button_plus_api.model import Connector, ConnectorEnum
from . import ButtonPlusHub

from .const import DOMAIN, MANUFACTURER
Expand All @@ -30,7 +31,7 @@ async def async_setup_entry(
active_connectors = active_connectors = [
connector.connector_id
for connector in hub.config.info.connectors
if connector.connector_type in [1, 2]
if connector.connector_type_enum() in [ConnectorEnum.DISPLAY, ConnectorEnum.BAR]
]

buttons = filter(lambda b: b.button_id // 2 in active_connectors, hub.config.mqtt_buttons)
Expand Down Expand Up @@ -58,11 +59,10 @@ def __init__(self, btn_id: int, hub: ButtonPlusHub):
self.unique_id = self.unique_id_gen()

def unique_id_gen(self):

match self._connector.connector_type:
case 1:
match self._connector.connector_type_enum():
case ConnectorEnum.BAR:
return self.unique_id_gen_bar()
case 2:
case ConnectorEnum.DISPLAY:
return self.unique_id_gen_display()

def unique_id_gen_bar(self):
Expand All @@ -81,25 +81,20 @@ def should_poll(self) -> bool:
return False

@property
def device_info(self):
def device_info(self) -> DeviceInfo:
"""Return information to link this entity with the correct device."""
device_info = {
"via_device": (DOMAIN, self._hub.hub_id),
"manufacturer": MANUFACTURER,
"identifiers" : {(DOMAIN, self.unique_id)}
}

match self._connector.connector_type:
case 1:
device_info["name"] = f"{self._hub_id} BAR Module {self._connector.connector_id}"
device_info["connections"] = {("bar_module", self._connector.connector_id)}
device_info["model"] = "BAR Module"
case 2:
device_info["name"] = f"{self._hub_id} Display Module"
device_info["connections"] = {("display_module", 1)}
device_info["model"] = "Display Module"

return device_info

identifiers: set[tuple[str, str]] = {}

match self._connector.connector_type_enum():
case ConnectorEnum.BAR:
identifiers = {(DOMAIN, f"{self._hub.hub_id} BAR Module {self._connector.connector_id}")}
case ConnectorEnum.DISPLAY:
identifiers = {(DOMAIN, f"{self._hub.hub_id} Display Module")}

return DeviceInfo(
identifiers=identifiers,
)

async def async_press(self) -> None:
"""Handle the button press."""
Expand Down
9 changes: 9 additions & 0 deletions custom_components/button_plus/button_plus_api/model.py
Original file line number Diff line number Diff line change
@@ -1,12 +1,21 @@
import json
from typing import List, Dict, Any
from enum import Enum

class ConnectorEnum(Enum):
NOT_CONNECTED = 0
BAR = 1
DISPLAY = 2


class Connector:
def __init__(self, connector_id: int, connector_type: int):
self.connector_id = connector_id
self.connector_type = connector_type

def connector_type_enum(self) -> ConnectorEnum:
return ConnectorEnum(self.connector_type)

@staticmethod
def from_dict(data: Dict[str, Any]) -> 'Connector':
return Connector(
Expand Down
72 changes: 64 additions & 8 deletions custom_components/button_plus/buttonplushub.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,15 +3,15 @@

import logging

from config.custom_components.button_plus.button_plus_api.local_api_client import LocalApiClient
from config.custom_components.button_plus.button_plus_api.model import ConnectorEnum, DeviceConfiguration
from config.custom_components.button_plus.const import DOMAIN, MANUFACTURER
from homeassistant.config_entries import ConfigEntry
from homeassistant.helpers import device_registry as dr

from .button_plus_api.local_api_client import LocalApiClient
from .button_plus_api.model import DeviceConfiguration
from homeassistant.core import HomeAssistant
from .const import DOMAIN, MANUFACTURER
from homeassistant.helpers import aiohttp_client


_LOGGER: logging.Logger = logging.getLogger(__package__)


Expand All @@ -22,8 +22,8 @@ def __init__(self, hass: HomeAssistant, config: DeviceConfiguration, entry: Conf
_LOGGER.debug(f"New hub with config {config.core}")
self._hass = hass
self.config = config
self._name = config.info.device_id
self._id = self.config.info.device_id
self._name = config.core.name or config.info.device_id
self._id = config.info.device_id
self._client = LocalApiClient(config.info.ip_address, aiohttp_client.async_get_clientsession(hass))
self.online = True
self.button_entities = {}
Expand All @@ -33,7 +33,7 @@ def __init__(self, hass: HomeAssistant, config: DeviceConfiguration, entry: Conf

device_registry = dr.async_get(hass)

device_registry.async_get_or_create(
self.device = device_registry.async_get_or_create(
configuration_url=f"http://{self.config.info.ip_address}/",
config_entry_id=entry.entry_id,
connections={(dr.CONNECTION_NETWORK_MAC, self.config.info.mac)},
Expand All @@ -42,9 +42,60 @@ def __init__(self, hass: HomeAssistant, config: DeviceConfiguration, entry: Conf
suggested_area=self.config.core.location,
name=self._name,
model="Base Module",
hw_version=config.info.firmware
sw_version=config.info.firmware
)

# 1 or none display module
self.display_module = next((self.create_display_module(hass, entry, self) for _ in self.connector(ConnectorEnum.DISPLAY)), None)
self.display_bar = [(connector_id, self.create_bar_module(hass, entry, self, connector_id)) for connector_id in self.connector(ConnectorEnum.BAR)]

def create_display_module(self, hass: HomeAssistant, entry: ConfigEntry, hub: ButtonPlusHub) -> None:
_LOGGER.debug(f"Add display module from '{hub.hub_id}'")
device_registry = dr.async_get(hass)

device = device_registry.async_get_or_create(
config_entry_id=entry.entry_id,
# connections={(DOMAIN, hub.config.info.device_id)},
name=f"{hub.name} Display Module",
model="Display Module",
manufacturer=MANUFACTURER,
suggested_area=hub.config.core.location,
identifiers={
(DOMAIN, f"{hub.hub_id} Display Module")
},
via_device=(DOMAIN, hub.hub_id)
)

return device


def create_bar_module(self, hass: HomeAssistant, entry: ConfigEntry, hub: ButtonPlusHub, connector_id: int) -> None:
_LOGGER.debug(f"Add bar module from '{hub.hub_id}' with connector '{connector_id}'")
device_registry = dr.async_get(hass)

device = device_registry.async_get_or_create(
config_entry_id=entry.entry_id,
# connections={(DOMAIN, hub.config.info.device_id)},
name=f"{hub._name} BAR Module {connector_id}",
model="Bar module",
manufacturer=MANUFACTURER,
suggested_area=hub.config.core.location,
identifiers={
(DOMAIN, f"{hub.hub_id} BAR Module {connector_id}")
},
via_device=(DOMAIN, hub.hub_id)
)

return device

def connector(self, connector_type: ConnectorEnum):
return [
connector.connector_id
for connector in self.config.info.connectors
if connector.connector_type_enum() in [connector_type]
]


@property
def client(self) -> LocalApiClient:
"""Return Button+ API client"""
Expand All @@ -54,6 +105,10 @@ def client(self) -> LocalApiClient:
def hub_id(self) -> str:
return self._id

@property
def name(self) -> str:
return self._name

def add_button(self, button_id, entity):
self.button_entities[str(button_id)] = entity

Expand All @@ -65,3 +120,4 @@ def add_top_label(self, button_id, entity):

def add_brightness(self, identifier, entity):
self.brightness_entities[identifier] = entity

3 changes: 2 additions & 1 deletion custom_components/button_plus/config_flow.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@
from json import JSONDecodeError

import voluptuous as vol
from custom_components.button_plus.button_plus_api.model import ConnectorEnum
from homeassistant import config_entries, exceptions
from homeassistant.const import CONF_IP_ADDRESS, CONF_EMAIL, CONF_PASSWORD, CONF_HOST
from homeassistant.helpers import aiohttp_client
Expand Down Expand Up @@ -284,7 +285,7 @@ def add_topics_to_buttons(self, device_config : DeviceConfiguration) -> DeviceCo
active_connectors = [
connector.connector_id
for connector in device_config.info.connectors
if connector.connector_type in [1, 2]
if connector.connector_type_enum() in [ConnectorEnum.DISPLAY, ConnectorEnum.BAR]
]

for button in filter(lambda b: b.button_id // 2 in active_connectors, device_config.mqtt_buttons):
Expand Down
9 changes: 6 additions & 3 deletions custom_components/button_plus/coordinator.py
Original file line number Diff line number Diff line change
@@ -1,12 +1,15 @@
import logging
import re

from homeassistant.components.button import ButtonEntity
from . import DOMAIN, ButtonPlusHub
from homeassistant.core import HomeAssistant, callback
from homeassistant.components.number import NumberEntity
from homeassistant.helpers.update_coordinator import DataUpdateCoordinator
from homeassistant.components.mqtt import client as mqtt, ReceiveMessage

import logging
import re
from custom_components.button_plus.buttonplushub import ButtonPlusHub
from custom_components.button_plus.const import DOMAIN


_LOGGER = logging.getLogger(__name__)

Expand Down
45 changes: 45 additions & 0 deletions custom_components/button_plus/device.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
from __future__ import annotations

from homeassistant.config_entries import ConfigEntry
from homeassistant.core import HomeAssistant
from homeassistant.helpers import device_registry as dr
from homeassistant.helpers.device_registry import DeviceInfo
from custom_components.button_plus.buttonplushub import ButtonPlusHub

from .const import DOMAIN, MANUFACTURER


class BarModuleDevice:
def __init__(self, hass: HomeAssistant, entry: ConfigEntry, hub: ButtonPlusHub, connector_id: int) -> None:
self.device_registry = dr.async_get(hass)

self.device = self.device_registry.async_get_or_create(
config_entry_id=entry.entry_id,
connections={(DOMAIN, hub.config.info.device_id)},
name=f"{hub._name} BAR Module {connector_id}",
model="Bar module",
manufacturer=MANUFACTURER,
suggested_area=hub.config.core.location,
identifiers={
(DOMAIN, f"{hub._hub_id} BAR Module {connector_id}")
},
via_device=(DOMAIN, hub.hub_id)
)

class DisplayModuleDevice:
def __init__(self, hass: HomeAssistant, entry: ConfigEntry, hub: ButtonPlusHub) -> None:
self.device_registry = dr.async_get(hass)

self.device = self.device_registry.async_get_or_create(
config_entry_id=entry.entry_id,
connections={(DOMAIN, hub.config.info.device_id)},
name=f"{hub._name} Display Module",
model="Display Module",
manufacturer=MANUFACTURER,
suggested_area=hub.config.core.location,
identifiers={
(DOMAIN, f"{hub._hub_id} Display Module")
},
via_device=(DOMAIN, hub.hub_id)
)

38 changes: 17 additions & 21 deletions custom_components/button_plus/number.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@
from homeassistant.core import HomeAssistant
from homeassistant.helpers.device_registry import DeviceInfo
from homeassistant.helpers.entity_platform import AddEntitiesCallback
from homeassistant.helpers.device_registry import DeviceInfo
from homeassistant.components.mqtt import client as mqtt
from .button_plus_api.event_type import EventType
from . import ButtonPlusHub
Expand Down Expand Up @@ -81,31 +82,26 @@ def update(self) -> None:
_LOGGER.debug(f"Update {self.name} (attr_name: {self._attr_name}) (unique: {self._attr_unique_id})")

@property
def device_info(self)-> DeviceInfo:
def device_info(self) -> DeviceInfo:
"""Return information to link this entity with the correct device."""
via_device = (DOMAIN, self._hub.hub_id)

identifiers: set[tuple[str, str]] = {}

match self.event_type:
case EventType.BRIGHTNESS_LARGE_DISPLAY:
name = f"{self._hub_id} Display Module"
connections: set[tuple[str, str]] = {("display_module", 1)}
model = "Display Module"
identifiers = {(DOMAIN, f'{self._hub.hub_id}_large_display_module')}
device_info = DeviceInfo(name=name, connections=connections, model=model, identifiers=identifiers, manufacturer=MANUFACTURER, via_device=via_device)
case EventType.BRIGHTNESS_MINI_DISPLAY:
name = f"{self._hub_id} BAR Module 1"
#name = f"Brightness Mini Display Module"
connections: set[tuple[str, str]] = {
#(DOMAIN, f'{self._hub.hub_id}_{self._btn_id}_bar_module_{self._connector.connector_id}')
("bar_module", 1),
("bar_module", 2),
("bar_module", 3)
}
model = "BAR Module"
identifiers = {(DOMAIN, f'{self._hub.hub_id}_display_module_mini_brightness')}
device_info = DeviceInfo(name=name, connections=connections, model=model, identifiers=identifiers, manufacturer=MANUFACTURER, via_device=via_device)

return device_info
# selects the first module it finds.
identifiers = {
(DOMAIN, f"{self._hub.hub_id} BAR Module 1"),
(DOMAIN, f"{self._hub.hub_id} BAR Module 2"),
(DOMAIN, f"{self._hub.hub_id} BAR Module 3")
}
case EventType.BRIGHTNESS_LARGE_DISPLAY:
identifiers = {(DOMAIN, f"{self._hub.hub_id} Display Module")}

return DeviceInfo(
identifiers=identifiers,
)



async def async_set_value(self, value: float) -> None:
Expand Down
Loading

0 comments on commit 826102c

Please sign in to comment.