Skip to content
This repository has been archived by the owner on Aug 25, 2023. It is now read-only.

Commit

Permalink
Add files via upload
Browse files Browse the repository at this point in the history
  • Loading branch information
LRvdLinden authored May 14, 2021
1 parent 2773a3b commit c65c920
Show file tree
Hide file tree
Showing 21 changed files with 1,399 additions and 0 deletions.
183 changes: 183 additions & 0 deletions config/custom_components/huesyncbox/__init__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,183 @@
"""The Philips Hue Play HDMI Sync Box integration."""
import asyncio
import logging
import json
import os

import voluptuous as vol

from homeassistant.config_entries import ConfigEntry
from homeassistant.core import HomeAssistant
from homeassistant.helpers import (config_validation as cv)
from homeassistant.helpers.config_validation import make_entity_service_schema
from homeassistant.helpers.service import async_extract_entity_ids
from homeassistant.components.light import ATTR_BRIGHTNESS, ATTR_BRIGHTNESS_STEP

from .huesyncbox import HueSyncBox, async_remove_entry_from_huesyncbox
from .const import DOMAIN, LOGGER, ATTR_SYNC, ATTR_SYNC_TOGGLE, ATTR_MODE, ATTR_MODE_NEXT, ATTR_MODE_PREV, MODES, ATTR_INTENSITY, ATTR_INTENSITY_NEXT, ATTR_INTENSITY_PREV, INTENSITIES, ATTR_INPUT, ATTR_INPUT_NEXT, ATTR_INPUT_PREV, INPUTS, ATTR_ENTERTAINMENT_AREA, SERVICE_SET_SYNC_STATE, SERVICE_SET_BRIGHTNESS, SERVICE_SET_MODE, SERVICE_SET_INTENSITY, SERVICE_SET_ENTERTAINMENT_AREA

CONFIG_SCHEMA = vol.Schema({DOMAIN: vol.Schema({})}, extra=vol.ALLOW_EXTRA)

PLATFORMS = ["media_player"]

HUESYNCBOX_SET_STATE_SCHEMA = make_entity_service_schema(
{
vol.Optional(ATTR_SYNC): cv.boolean,
vol.Optional(ATTR_SYNC_TOGGLE): cv.boolean,
vol.Optional(ATTR_BRIGHTNESS): cv.small_float,
vol.Optional(ATTR_BRIGHTNESS_STEP): vol.All(vol.Coerce(float), vol.Range(min=-1, max=1)),
vol.Optional(ATTR_MODE): vol.In(MODES),
vol.Optional(ATTR_MODE_NEXT): cv.boolean,
vol.Optional(ATTR_MODE_PREV): cv.boolean,
vol.Optional(ATTR_INTENSITY): vol.In(INTENSITIES),
vol.Optional(ATTR_INTENSITY_NEXT): cv.boolean,
vol.Optional(ATTR_INTENSITY_PREV): cv.boolean,
vol.Optional(ATTR_INPUT): vol.In(INPUTS),
vol.Optional(ATTR_INPUT_NEXT): cv.boolean,
vol.Optional(ATTR_INPUT_PREV): cv.boolean,
vol.Optional(ATTR_ENTERTAINMENT_AREA): cv.string,
}
)

HUESYNCBOX_SET_BRIGHTNESS_SCHEMA = make_entity_service_schema(
{vol.Required(ATTR_BRIGHTNESS): cv.small_float}
)

HUESYNCBOX_SET_MODE_SCHEMA = make_entity_service_schema(
{vol.Required(ATTR_MODE): vol.In(MODES)}
)

HUESYNCBOX_SET_INTENSITY_SCHEMA = make_entity_service_schema(
{vol.Required(ATTR_INTENSITY): vol.In(INTENSITIES), vol.Optional(ATTR_MODE): vol.In(MODES)}
)

HUESYNCBOX_SET_ENTERTAINMENT_AREA_SCHEMA = make_entity_service_schema(
{vol.Required(ATTR_ENTERTAINMENT_AREA): cv.string}
)

services_registered = False

async def async_setup(hass: HomeAssistant, config: dict):
"""
Set up the Philips Hue Play HDMI Sync Box integration.
Only supporting zeroconf, so nothing to do here.
"""
hass.data[DOMAIN] = {}

return True

async def async_setup_entry(hass: HomeAssistant, entry: ConfigEntry):
"""Set up a config entry for Philips Hue Play HDMI Sync Box."""

LOGGER.debug("%s async_setup_entry\nentry:\n%s\nhass.data\n%s" % (__name__, str(entry), hass.data[DOMAIN]))

huesyncbox = HueSyncBox(hass, entry)
hass.data[DOMAIN][entry.data["unique_id"]] = huesyncbox

if not await huesyncbox.async_setup():
return False

for platform in PLATFORMS:
hass.async_create_task(
hass.config_entries.async_forward_entry_setup(entry, platform)
)

# Register services on first entry
global services_registered
if not services_registered:
await async_register_services(hass)
services_registered = True

return True

async def async_unload_entry(hass: HomeAssistant, entry: ConfigEntry):
"""Unload a config entry."""
unload_ok = all(
await asyncio.gather(
*[
hass.config_entries.async_forward_entry_unload(entry, platform)
for platform in PLATFORMS
]
)
)

if unload_ok:
huesyncbox = hass.data[DOMAIN].pop(entry.data["unique_id"])
await huesyncbox.async_reset()

# Unregister services when last entry is unloaded
if len(hass.data[DOMAIN].items()) == 0:
await async_unregister_services(hass)
global services_registered
services_registered = False

return unload_ok


async def async_remove_entry(hass: HomeAssistant, entry: ConfigEntry) -> None:
# Best effort cleanup. User might not even have the device anymore or had it factory reset.
# Note that the entry already has been unloaded.
try:
await async_remove_entry_from_huesyncbox(entry)
except Exception as e:
LOGGER.warning("Unregistering Philips Hue Play HDMI Sync Box failed: %s ", e)


async def async_register_services(hass: HomeAssistant):
async def async_set_sync_state(call):
entity_ids = await async_extract_entity_ids(hass, call)
for _, entry in hass.data[DOMAIN].items():
if entry.entity and entry.entity.entity_id in entity_ids:
await entry.entity.async_set_sync_state(call.data)

hass.services.async_register(
DOMAIN, SERVICE_SET_SYNC_STATE, async_set_sync_state, schema=HUESYNCBOX_SET_STATE_SCHEMA
)

async def async_set_sync_mode(call):
entity_ids = await async_extract_entity_ids(hass, call)
for _, entry in hass.data[DOMAIN].items():
if entry.entity and entry.entity.entity_id in entity_ids:
await entry.entity.async_set_sync_mode(call.data.get(ATTR_MODE))

hass.services.async_register(
DOMAIN, SERVICE_SET_MODE, async_set_sync_mode, schema=HUESYNCBOX_SET_MODE_SCHEMA
)

async def async_set_intensity(call):
entity_ids = await async_extract_entity_ids(hass, call)
for _, entry in hass.data[DOMAIN].items():
if entry.entity and entry.entity.entity_id in entity_ids:
await entry.entity.async_set_intensity(call.data.get(ATTR_INTENSITY), call.data.get(ATTR_MODE, None))

hass.services.async_register(
DOMAIN, SERVICE_SET_INTENSITY, async_set_intensity, schema=HUESYNCBOX_SET_INTENSITY_SCHEMA
)

async def async_set_brightness(call):
entity_ids = await async_extract_entity_ids(hass, call)
for _, entry in hass.data[DOMAIN].items():
if entry.entity and entry.entity.entity_id in entity_ids:
await entry.entity.async_set_brightness(call.data.get(ATTR_BRIGHTNESS))

hass.services.async_register(
DOMAIN, SERVICE_SET_BRIGHTNESS, async_set_brightness, schema=HUESYNCBOX_SET_BRIGHTNESS_SCHEMA
)

async def async_set_entertainment_area(call):
entity_ids = await async_extract_entity_ids(hass, call)
for _, entry in hass.data[DOMAIN].items():
if entry.entity and entry.entity.entity_id in entity_ids:
await entry.entity.async_select_entertainment_area(call.data.get(ATTR_ENTERTAINMENT_AREA))

hass.services.async_register(
DOMAIN, SERVICE_SET_ENTERTAINMENT_AREA, async_set_entertainment_area, schema=HUESYNCBOX_SET_ENTERTAINMENT_AREA_SCHEMA
)


async def async_unregister_services(hass):
hass.services.async_remove(DOMAIN, SERVICE_SET_SYNC_STATE)
hass.services.async_remove(DOMAIN, SERVICE_SET_BRIGHTNESS)
hass.services.async_remove(DOMAIN, SERVICE_SET_MODE)
hass.services.async_remove(DOMAIN, SERVICE_SET_INTENSITY)
hass.services.async_remove(DOMAIN, SERVICE_SET_ENTERTAINMENT_AREA)
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
117 changes: 117 additions & 0 deletions config/custom_components/huesyncbox/config_flow.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,117 @@
"""Config flow for Philips Hue Play HDMI Sync Box integration."""
import asyncio
import logging

import voluptuous as vol

from homeassistant import core, config_entries, exceptions
from homeassistant.core import callback

from .const import DOMAIN, LOGGER # pylint:disable=unused-import
from .errors import AuthenticationRequired, CannotConnect
from .huesyncbox import async_get_aiohuesyncbox_from_entry_data, async_register_aiohuesyncbox


class ConfigFlow(config_entries.ConfigFlow, domain=DOMAIN):
"""Handle a config flow for Philips Hue Play HDMI Sync Box."""

VERSION = 1
CONNECTION_CLASS = config_entries.CONN_CLASS_LOCAL_POLL

def __init__(self):
"""Initialize the config flow."""
pass

async def async_step_user(self, user_input=None):
"""Handle a flow initialized by the user."""
return self.async_abort(reason="manual_not_supported")


async def async_step_link(self, user_input=None):
"""
Attempt to link with the huesyncbox.
We will only end up in this step when the token is invalid.
"""
if user_input is None:
return self.async_show_form(step_id="link")

errors = {}

try:
api = await async_get_aiohuesyncbox_from_entry_data(self.context)
result = await async_register_aiohuesyncbox(self.hass, api)
self.context['access_token'] = result['access_token']
self.context['registration_id'] = result['registration_id']
await api.close()

return await self._async_create_entry_from_context()
except AuthenticationRequired:
errors["base"] = "register_failed"
except CannotConnect:
errors["base"] = "cannot_connect"
except Exception: # pylint: disable=broad-except
LOGGER.exception(
"Unknown error connecting to the Phlips Hue Play HDMI Sync Box %s at %s", self.context["unique_id"], self.context["host"]
)
errors["base"] = "unknown"

return self.async_show_form(step_id="link", errors=errors)

async def async_step_zeroconf(self, discovery_info):
"""Handle zeroconf discovery."""

entry_info = {
"host": discovery_info["host"],
"port": discovery_info["port"],
"path": discovery_info["properties"]["path"],
"unique_id": discovery_info["properties"]["uniqueid"],
"name": discovery_info["properties"]["name"],
"devicetype": discovery_info["properties"]["devicetype"]
}

return await self.async_step_check(entry_info)


async def async_step_check(self, entry_info):
"""Perform some checks and create entry if OK."""

await self.async_set_unique_id(entry_info['unique_id'])
self._abort_if_unique_id_configured(updates=entry_info)

self.context["host"] = entry_info["host"]
self.context["unique_id"] = entry_info["unique_id"]
self.context["port"] = entry_info["port"]
self.context["name"] = entry_info["name"]
self.context["path"] = entry_info["path"]
self.context['access_token'] = entry_info.get('access_token')
self.context['registration_id'] = entry_info.get('registration_id')

self.context["title_placeholders"] = {"name": self.context["name"], "unique_id": self.context["unique_id"]}

api = await async_get_aiohuesyncbox_from_entry_data(entry_info)
if await api.is_registered():
await api.close()
return await self._async_create_entry_from_context()

return await self.async_step_link()


async def _async_create_entry_from_context(self):
"""Return create entry with data from context."""
entry_data = {
"host": self.context["host"],
"name": self.context["name"],
"path": self.context["path"],
"port": self.context["port"],
"unique_id": self.context["unique_id"],
"access_token": self.context["access_token"],
"registration_id": self.context["registration_id"],
}

LOGGER.debug("%s _async_create_entry_from_context\entry_data:\n%s\n" % (__name__, entry_data))

return self.async_create_entry(
# Title should identify this entry, so use device name and lets include unique_id in case of multiple devices with the same name
title=f"{entry_data['name']} ({entry_data['unique_id']})",
data=entry_data,
)
51 changes: 51 additions & 0 deletions config/custom_components/huesyncbox/const.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
"""Constants for the Philips Hue Play HDMI Sync Box integration."""

import logging

LOGGER = logging.getLogger(__package__)
DOMAIN = "huesyncbox"

MANUFACTURER_NAME = "Signify"

SERVICE_SET_SYNC_STATE = 'set_sync_state'
SERVICE_SET_BRIGHTNESS = 'set_brightness'
SERVICE_SET_INTENSITY = 'set_intensity'
SERVICE_SET_MODE = 'set_sync_mode'
SERVICE_SET_ENTERTAINMENT_AREA = 'set_entertainment_area'

ATTR_SYNC = 'sync'
ATTR_SYNC_TOGGLE = 'sync_toggle'

ATTR_MODE = 'mode'
ATTR_MODE_NEXT = 'mode_next'
ATTR_MODE_PREV = 'mode_prev'

MODE_VIDEO = 'video'
MODE_MUSIC = 'music'
MODE_GAME = 'game'

MODES = [ MODE_VIDEO, MODE_MUSIC, MODE_GAME ]

ATTR_INTENSITY = "intensity"
ATTR_INTENSITY_NEXT = "intensity_next"
ATTR_INTENSITY_PREV = "intensity_prev"

INTENSITY_SUBTLE = "subtle"
INTENSITY_MODERATE = "moderate"
INTENSITY_HIGH = "high"
INTENSITY_INTENSE = "intense"

INTENSITIES = [ INTENSITY_SUBTLE, INTENSITY_MODERATE, INTENSITY_HIGH, INTENSITY_INTENSE ]

ATTR_INPUT = "input"
ATTR_INPUT_NEXT = "input_next"
ATTR_INPUT_PREV = "input_prev"

INPUT_HDMI1 = 'input1'
INPUT_HDMI2 = 'input2'
INPUT_HDMI3 = 'input3'
INPUT_HDMI4 = 'input4'

INPUTS = [ INPUT_HDMI1, INPUT_HDMI2, INPUT_HDMI3, INPUT_HDMI4 ]

ATTR_ENTERTAINMENT_AREA = "entertainment_area"
Loading

0 comments on commit c65c920

Please sign in to comment.