Skip to content

Instantly share code, notes, and snippets.

@joshspicer
Last active January 15, 2025 00:52
Show Gist options
  • Save joshspicer/e0992ceffa2d55576363b2d1ae22dd8d to your computer and use it in GitHub Desktop.
Save joshspicer/e0992ceffa2d55576363b2d1ae22dd8d to your computer and use it in GitHub Desktop.
Minimal Home Assistant Integration to control an ELK-BLEDOM Bluetooth Device (https://joshspicer.com/ELK-BLEDOM)
"""Config flow for On Air Sign integration."""
from __future__ import annotations
import voluptuous as vol
from homeassistant import config_entries
from .const import DOMAIN, DEVICE_ADDRESS
class OnAirSignConfigFlow(config_entries.ConfigFlow, domain=DOMAIN):
"""Handle a config flow for On Air Sign."""
VERSION = 1
async def async_step_user(self, user_input=None):
"""Handle the initial step."""
if user_input is not None:
await self.async_set_unique_id(DEVICE_ADDRESS)
self._abort_if_unique_id_configured()
return self.async_create_entry(
title="On Air Sign",
data={"address": DEVICE_ADDRESS}
)
return self.async_show_form(
step_id="user",
data_schema=vol.Schema({}),
description_placeholders={
"device_id": DEVICE_ADDRESS,
},
)
"""The On Air Sign integration."""
from homeassistant.config_entries import ConfigEntry
from homeassistant.core import HomeAssistant
from .const import DOMAIN
PLATFORMS: list[str] = ["light"]
async def async_setup_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool:
"""Set up Bledob BLE Light from a config entry."""
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."""
return await hass.config_entries.async_unload_platforms(entry, PLATFORMS)
"""Config flow for On Air Sign integration."""
from __future__ import annotations
import voluptuous as vol
from homeassistant import config_entries
from .const import DOMAIN, DEVICE_ADDRESS
class OnAirSignConfigFlow(config_entries.ConfigFlow, domain=DOMAIN):
"""Handle a config flow for On Air Sign."""
VERSION = 1
async def async_step_user(self, user_input=None):
"""Handle the initial step."""
if user_input is not None:
await self.async_set_unique_id(DEVICE_ADDRESS)
self._abort_if_unique_id_configured()
return self.async_create_entry(
title="On Air Sign",
data={"address": DEVICE_ADDRESS}
)
return self.async_show_form(
step_id="user",
data_schema=vol.Schema({}),
description_placeholders={
"device_id": DEVICE_ADDRESS,
},
)
"""Constants for the On Air Sign integration."""
DOMAIN = "on-air-sign-btle"
# BLE Device
DEVICE_ADDRESS = "<YOUR_DEVICE_MAC>"
CHARACTERISTIC_UUID = "0000fff3-0000-1000-8000-00805f9b34fb"
# Commands
CMD_TURN_OFF = bytes.fromhex('7e0404000000ff00ef')
CMD_TURN_ON = bytes.fromhex('7e0404f00001ff00ef')
CMD_COLOR_WHITE = bytes.fromhex('7e070503ffffff10ef')
def generate_color_command(red: int, green: int, blue: int) -> bytes:
"""Generate a color command from RGB values."""
return bytes.fromhex(f'7e070503{red:02x}{green:02x}{blue:02x}10ef')
"""Platform for On Air Sign integration."""
from __future__ import annotations
import logging
from bleak import BleakClient
from homeassistant.components.light import (
LightEntity,
ColorMode,
)
from homeassistant.config_entries import ConfigEntry
from homeassistant.core import HomeAssistant
from homeassistant.helpers.entity_platform import AddEntitiesCallback
from .const import (
CHARACTERISTIC_UUID,
CMD_TURN_ON,
CMD_TURN_OFF,
DOMAIN,
generate_color_command,
)
_LOGGER = logging.getLogger(__name__)
async def async_setup_entry(
hass: HomeAssistant,
entry: ConfigEntry,
async_add_entities: AddEntitiesCallback,
) -> None:
"""Set up the Bledob Light platform."""
async_add_entities([BledobLight(entry.data["address"])], True)
class BledobLight(LightEntity):
"""Representation of a Bledob Light."""
def __init__(self, address: str) -> None:
"""Initialize the light."""
self._address = address
self._attr_unique_id = address
self._attr_is_on = False
self._attr_name = "On Air Sign"
self._attr_supported_color_modes = {ColorMode.RGB}
self._attr_color_mode = ColorMode.RGB
self._attr_rgb_color = (0, 0, 0)
async def async_turn_on(self, **kwargs) -> None:
"""Turn on the light."""
try:
async with BleakClient(self._address) as client:
if "rgb_color" in kwargs:
self._attr_rgb_color = kwargs["rgb_color"]
r, g, b = self._attr_rgb_color
cmd = generate_color_command(r, g, b)
await client.write_gatt_char(CHARACTERISTIC_UUID, cmd)
else:
await client.write_gatt_char(CHARACTERISTIC_UUID, CMD_TURN_ON)
self._attr_is_on = True
except Exception as error:
_LOGGER.error("Error turning on light: %s", error)
async def async_turn_off(self, **kwargs) -> None:
"""Turn off the light."""
try:
async with BleakClient(self._address) as client:
await client.write_gatt_char(CHARACTERISTIC_UUID, CMD_TURN_OFF)
self._attr_is_on = False
except Exception as error:
_LOGGER.error("Error turning off light: %s", error)
{
"domain": "on-air-sign-btle",
"name": "On Air Sign",
"bluetooth": [
{
"service_uuid": "0000fff0-0000-1000-8000-00805f9b34fb",
"connectable": true
}
],
"codeowners": ["@joshspicer"],
"config_flow": true,
"dependencies": ["bluetooth_adapters"],
"documentation": "https://joshspicer.com/ELK-BLEDOM",
"iot_class": "local_push",
"requirements": ["bleak"],
"version": "0.1.0"
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment