Skip to content

Commit

Permalink
Merge pull request #42 from P-Storm/feat/brightness
Browse files Browse the repository at this point in the history
Added brightness and start pages
  • Loading branch information
koenhendriks authored Mar 10, 2024
2 parents 84d43d2 + 5de1ade commit 7576c8e
Show file tree
Hide file tree
Showing 8 changed files with 266 additions and 12 deletions.
2 changes: 1 addition & 1 deletion custom_components/button_plus/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@

# List of platforms to support. There should be a matching .py file for each,
# eg <cover.py> and <sensor.py>
PLATFORMS: list[str] = ["button", "text"]
PLATFORMS: list[str] = ["button", "text", "number"]


async def async_setup_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool:
Expand Down
4 changes: 4 additions & 0 deletions custom_components/button_plus/button_plus_api/event_type.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
class EventType(int, Enum):
CLICK = 0
LONG_PRESS = 1
PAGE_STATUS = 6
BLUE_LED = 8
RED_LED = 9
GREEN_LED = 10
Expand All @@ -14,3 +15,6 @@ class EventType(int, Enum):
VALUE = 15
UNIT = 17
SENSOR_VALUE = 18
SET_PAGE = 20
BRIGHTNESS_LARGE_DISPLAY = 24
BRIGHTNESS_MINI_DISPLAY = 25
7 changes: 5 additions & 2 deletions custom_components/button_plus/button_plus_api/model.py
Original file line number Diff line number Diff line change
Expand Up @@ -54,7 +54,8 @@ def from_dict(data: Dict[str, Any]) -> 'Info':

class Core:
def __init__(self, name: str, location: str, auto_backup: bool, brightness_large_display: int,
brightness_mini_display: int, led_color_front: int, led_color_wall: int, color: int):
brightness_mini_display: int, led_color_front: int, led_color_wall: int, color: int,
topics: List[Dict[str, Any]]):
self.name = name
self.location = location
self.auto_backup = auto_backup
Expand All @@ -63,6 +64,7 @@ def __init__(self, name: str, location: str, auto_backup: bool, brightness_large
self.led_color_front = led_color_front
self.led_color_wall = led_color_wall
self.color = color
self.topics = topics

@staticmethod
def from_dict(data: Dict[str, Any]) -> 'Core':
Expand All @@ -74,7 +76,8 @@ def from_dict(data: Dict[str, Any]) -> 'Core':
brightness_mini_display=data['brightnessminidisplay'],
led_color_front=data['ledcolorfront'],
led_color_wall=data['ledcolorwall'],
color=data['color']
color=data['color'],
topics=data['topics']
)


Expand Down
4 changes: 4 additions & 0 deletions custom_components/button_plus/buttonplushub.py
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@ def __init__(self, hass: HomeAssistant, config: DeviceConfiguration, entry: Conf
self.button_entities = {}
self.label_entities = {}
self.top_label_entities = {}
self.brightness_entities = {}

device_registry = dr.async_get(hass)

Expand Down Expand Up @@ -61,3 +62,6 @@ def add_label(self, button_id, entity):

def add_top_label(self, button_id, entity):
self.top_label_entities[str(button_id)] = entity

def add_brightness(self, identifier, entity):
self.brightness_entities[identifier] = entity
40 changes: 39 additions & 1 deletion custom_components/button_plus/config_flow.py
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@
from .button_plus_api.model import DeviceConfiguration, MqttBroker
from .button_plus_api.event_type import EventType
from homeassistant.helpers.network import get_url
from packaging import version

from .const import DOMAIN # pylint:disable=unused-import

Expand Down Expand Up @@ -99,6 +100,7 @@ async def async_step_manual(self, user_input=None):
device_config: DeviceConfiguration = DeviceConfiguration.from_json(json_config)

self.add_broker_to_config(device_config)
self.add_topics_to_core(device_config)
self.add_topics_to_buttons(device_config)

await api_client.push_config(device_config)
Expand Down Expand Up @@ -240,7 +242,43 @@ def add_broker_to_config(self, device_config: DeviceConfiguration) -> DeviceConf
device_config.mqtt_brokers.append(broker)
return device_config

def add_topics_to_buttons(self, device_config) -> DeviceConfiguration:
def add_topics_to_core(self, device_config : DeviceConfiguration) -> DeviceConfiguration:
device_id = device_config.info.device_id
min_version = "1.11"

if version.parse(device_config.info.firmware) < version.parse(min_version):
_LOGGER.debug(f"Current version {device_config.info.firmware} doesn't support the brightness, it must be at least firmware version {min_version}")
return

device_config.core.topics.append({
"brokerid": "ha-button-plus",
"topic": f"buttonplus/{device_id}/brightness/large",
"payload": "",
"eventtype": EventType.BRIGHTNESS_LARGE_DISPLAY
})

device_config.core.topics.append({
"brokerid": "ha-button-plus",
"topic": f"buttonplus/{device_id}/brightness/mini",
"payload": "",
"eventtype": EventType.BRIGHTNESS_MINI_DISPLAY
})

device_config.core.topics.append({
"brokerid": "ha-button-plus",
"topic": f"buttonplus/{device_id}/page/status",
"payload": "",
"eventtype": EventType.PAGE_STATUS
})

device_config.core.topics.append({
"brokerid": "ha-button-plus",
"topic": f"buttonplus/{device_id}/page/set",
"payload": "",
"eventtype": EventType.SET_PAGE
})

def add_topics_to_buttons(self, device_config : DeviceConfiguration) -> DeviceConfiguration:
device_id = device_config.info.device_id

active_connectors = [
Expand Down
50 changes: 49 additions & 1 deletion custom_components/button_plus/coordinator.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
from homeassistant.components.button import ButtonEntity
from . import DOMAIN, ButtonPlusHub
from homeassistant.core import HomeAssistant
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

Expand All @@ -26,6 +27,8 @@ def __init__(self, hass: HomeAssistant, hub: ButtonPlusHub):
self._hass = hass
self._mqtt_subscribed_buttons = False
self._mqtt_topic_buttons = f"buttonplus/{hub.hub_id}/button/+/click"
self._mqtt_topic_brightness = f"buttonplus/{hub.hub_id}/brightness/+"
self._mqtt_topic_page = f"buttonplus/{hub.hub_id}/page/+"

async def _async_update_data(self):
"""Create MQTT subscriptions for buttonplus """
Expand All @@ -39,6 +42,51 @@ async def _async_update_data(self):
)
_LOGGER.debug(f"MQTT subscribed to {self._mqtt_topic_buttons}")


if not self._mqtt_subscribed_buttons:
self.unsubscribe_mqtt = await mqtt.async_subscribe(
self._hass,
self._mqtt_topic_brightness,
self.mqtt_brightness_callback,
0
)
_LOGGER.debug(f"MQTT subscribed to {self._mqtt_topic_brightness}")


if not self._mqtt_subscribed_buttons:
self.unsubscribe_mqtt = await mqtt.async_subscribe(
self._hass,
self._mqtt_topic_page,
self.mqtt_page_callback,
0
)
_LOGGER.debug(f"MQTT subscribed to {self._mqtt_topic_page}")

@callback
async def mqtt_page_callback(self, message: ReceiveMessage):
# Handle the message here
_LOGGER.debug(f"Received message on topic {message.topic}: {message.payload}")
match = re.search(r'/page/(\w+)', message.topic)
# is 'status' or 'set'
page_type = match.group(1)

# TODO: implement page control


@callback
async def mqtt_brightness_callback(self, message: ReceiveMessage):
# Handle the message here
_LOGGER.debug(f"Received message on topic {message.topic}: {message.payload}")
match = re.search(r'/brightness/(\w+)', message.topic)
brightness_type = match.group(1)

entity: NumberEntity = self.hub.brightness_entities[brightness_type]

value = float(message.payload)
entity._attr_native_value = value
entity.schedule_update_ha_state()

@callback
async def mqtt_button_callback(self, message: ReceiveMessage):
# Handle the message here
_LOGGER.debug(f"Received message on topic {message.topic}: {message.payload}")
Expand Down
143 changes: 143 additions & 0 deletions custom_components/button_plus/number.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,143 @@
""" Platform for light integration. """
from __future__ import annotations

import logging
from typing import Any

from homeassistant.components.number import NumberEntity
from homeassistant.config_entries import ConfigEntry
from homeassistant.core import HomeAssistant
from homeassistant.helpers.device_registry import DeviceInfo
from homeassistant.helpers.entity_platform import AddEntitiesCallback
from homeassistant.components.mqtt import client as mqtt
from .button_plus_api.event_type import EventType
from . import ButtonPlusHub
from packaging import version

from .const import DOMAIN, MANUFACTURER

_LOGGER = logging.getLogger(__name__)



async def async_setup_entry(
hass: HomeAssistant,
config_entry: ConfigEntry,
async_add_entities: AddEntitiesCallback,
) -> None:

brightness = []

"""Add switches for passed config_entry in HA."""

hub: ButtonPlusHub = hass.data[DOMAIN][config_entry.entry_id]

if version.parse(hub.config.info.firmware) < version.parse('1.11'):
_LOGGER.info(f"Current version {hub.config.info.firmware} doesn't support the brightness, it must be at least firmware version {min_version}")
return

_LOGGER.debug(f"Creating number with parameters: {hub.hub_id}")
# _LOGGER.debug(f"Creating Lights with parameters: {button.button_id} {button.label} {hub.hub_id}")
mini = ButtonPlusMiniBrightness(hub)
brightness.append(mini)
hub.add_brightness("mini", mini)

large = ButtonPlusLargeBrightness(hub)
brightness.append(large)
hub.add_brightness("large", mini)

async_add_entities(brightness)



class ButtonPlusBrightness(NumberEntity):
def __init__(self, hub: ButtonPlusHub, brightness_type: str, event_type: EventType):
self._hub = hub
self._hub_id = hub.hub_id
self._brightness_type = brightness_type
self.entity_id = f"brightness.{brightness_type}_{self._hub_id}"
self._attr_name = f'brightness-{brightness_type}'
self.event_type = event_type
self._topics = hub.config.core.topics
self._attr_icon = "mdi:television-ambient-light"
self._attr_unique_id = f'brightness_{brightness_type}-{self._hub_id}'

@property
def native_max_value(self) -> float:
return 100

@property
def native_min_value(self) -> float:
return 0

@property
def native_unit_of_measurement(self) -> str:
return "%"

def update(self) -> None:
"""Fetch new state data for this light."""
# get latest stats from mqtt for this light
# then update self._state
_LOGGER.debug(f"Update {self.name} (attr_name: {self._attr_name}) (unique: {self._attr_unique_id})")

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

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


async def async_set_value(self, value: float) -> None:
"""Set the text value and publish to mqtt."""
# TODO: Add support for mini
label_topic = f"buttonplus/{self._hub_id}/brightness/{self._brightness_type}"
_LOGGER.debug(f"ButtonPlus brightness update for {self.entity_id}")
_LOGGER.debug(f"ButtonPlus brightness update to {label_topic} with new value: {value}")
await mqtt.async_publish(hass=self.hass, topic=label_topic, payload=value, qos=0, retain=True)
self._attr_native_value = value
self.async_write_ha_state()


class ButtonPlusMiniBrightness(ButtonPlusBrightness):
""" Numeric entity representation """

def __init__(self, hub: ButtonPlusHub):
super().__init__(hub, "mini", EventType.BRIGHTNESS_MINI_DISPLAY)

@property
def name(self) -> str:
"""Return the display name of this light."""
return f'Brightness mini display'


class ButtonPlusLargeBrightness(ButtonPlusBrightness):
""" Numeric entity representation """

def __init__(self, hub: ButtonPlusHub):
super().__init__(hub, "large", EventType.BRIGHTNESS_LARGE_DISPLAY)

@property
def name(self) -> str:
"""Return the display name of this light."""
return f'Brightness large display'
Loading

0 comments on commit 7576c8e

Please sign in to comment.