Skip to content

Commit

Permalink
deako integration using pydeako
Browse files Browse the repository at this point in the history
  • Loading branch information
Balake committed Oct 18, 2023
1 parent d8e541a commit 64c264f
Show file tree
Hide file tree
Showing 18 changed files with 688 additions and 0 deletions.
1 change: 1 addition & 0 deletions .strict-typing
Original file line number Diff line number Diff line change
Expand Up @@ -94,6 +94,7 @@ homeassistant.components.configurator.*
homeassistant.components.cover.*
homeassistant.components.cpuspeed.*
homeassistant.components.crownstone.*
homeassistant.components.deako.*
homeassistant.components.deconz.*
homeassistant.components.demo.*
homeassistant.components.derivative.*
Expand Down
2 changes: 2 additions & 0 deletions CODEOWNERS
Validating CODEOWNERS rules …
Original file line number Diff line number Diff line change
Expand Up @@ -244,6 +244,8 @@ build.json @home-assistant/supervisor
/tests/components/date/ @home-assistant/core
/homeassistant/components/datetime/ @home-assistant/core
/tests/components/datetime/ @home-assistant/core
/homeassistant/components/deako/ @sebirdman @balake @deakolights
/tests/components/deako/ @sebirdman @balake @deakolights
/homeassistant/components/debugpy/ @frenck
/tests/components/debugpy/ @frenck
/homeassistant/components/deconz/ @Kane610
Expand Down
60 changes: 60 additions & 0 deletions homeassistant/components/deako/__init__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,60 @@
"""The deako integration."""
from __future__ import annotations

import logging

from pydeako.deako import Deako, FindDevicesTimeout
from pydeako.discover import DeakoDiscoverer

from homeassistant.components import zeroconf
from homeassistant.config_entries import ConfigEntry
from homeassistant.const import Platform
from homeassistant.core import HomeAssistant
from homeassistant.exceptions import ConfigEntryNotReady

from .const import DOMAIN

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

PLATFORMS: list[Platform] = [Platform.LIGHT]


async def async_setup_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool:
"""Set up deako from a config entry."""
hass_data = hass.data.setdefault(DOMAIN, {})
if hass_data is None or not isinstance(hass_data, dict):
hass_data = {}
hass.data[DOMAIN] = hass_data

_zc = await zeroconf.async_get_instance(hass)
discoverer = DeakoDiscoverer(_zc)

connection = Deako(discoverer.get_address)
await connection.connect()
try:
await connection.find_devices()
except FindDevicesTimeout as exc:
_LOGGER.warning("No devices expected")
await connection.disconnect()
raise ConfigEntryNotReady(exc) from exc

devices = connection.get_devices()
if len(devices) == 0:
await connection.disconnect()
raise ConfigEntryNotReady(devices)

hass.data[DOMAIN][entry.entry_id] = connection

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."""
await hass.data[DOMAIN][entry.entry_id].disconnect()

if unload_ok := await hass.config_entries.async_unload_platforms(entry, PLATFORMS):
hass.data[DOMAIN].pop(entry.entry_id)

return unload_ok
26 changes: 26 additions & 0 deletions homeassistant/components/deako/config_flow.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
"""Config flow for deako."""

from pydeako.discover import DeakoDiscoverer, DevicesNotFoundException

from homeassistant.components import zeroconf
from homeassistant.core import HomeAssistant
from homeassistant.helpers import config_entry_flow

from .const import DOMAIN, NAME


async def _async_has_devices(hass: HomeAssistant) -> bool:
"""Return if there are devices that can be discovered."""
_zc = await zeroconf.async_get_instance(hass)
discoverer = DeakoDiscoverer(_zc)

Check warning on line 15 in homeassistant/components/deako/config_flow.py

View check run for this annotation

Codecov / codecov/patch

homeassistant/components/deako/config_flow.py#L14-L15

Added lines #L14 - L15 were not covered by tests

try:
await discoverer.get_address()

Check warning on line 18 in homeassistant/components/deako/config_flow.py

View check run for this annotation

Codecov / codecov/patch

homeassistant/components/deako/config_flow.py#L17-L18

Added lines #L17 - L18 were not covered by tests
# address exists, there's at least one device
return True

Check warning on line 20 in homeassistant/components/deako/config_flow.py

View check run for this annotation

Codecov / codecov/patch

homeassistant/components/deako/config_flow.py#L20

Added line #L20 was not covered by tests

except DevicesNotFoundException:
return False

Check warning on line 23 in homeassistant/components/deako/config_flow.py

View check run for this annotation

Codecov / codecov/patch

homeassistant/components/deako/config_flow.py#L22-L23

Added lines #L22 - L23 were not covered by tests


config_entry_flow.register_discovery_flow(DOMAIN, NAME, _async_has_devices)
14 changes: 14 additions & 0 deletions homeassistant/components/deako/const.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
"""Constants for Deako."""
# Base component constants
NAME = "Deako"
DOMAIN = "deako"
DOMAIN_DATA = f"{DOMAIN}_data"

# Icons
ICON = "mdi:format-quote-close"

# Platforms
LIGHT = "light"
PLATFORMS = [LIGHT]

CONNECTION_ID = "connection_id"
116 changes: 116 additions & 0 deletions homeassistant/components/deako/light.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,116 @@
"""Binary sensor platform for integration_blueprint."""
import logging
from typing import Any

from pydeako.deako import Deako

from homeassistant.components.light import ATTR_BRIGHTNESS, ColorMode, LightEntity
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 .const import DOMAIN

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


async def async_setup_entry(
hass: HomeAssistant,
config: ConfigEntry,
add_entities: AddEntitiesCallback,
) -> None:
"""Configure the platform."""
client: Deako = hass.data[DOMAIN][config.entry_id]

devices = client.get_devices()
if len(devices) == 0:
# If deako devices are advertising on mdns, we should be able to get at least one device
_LOGGER.warning("No devices found from local integration")
await client.disconnect()
return
lights = [DeakoLightSwitch(client, uuid) for uuid in devices]
add_entities(lights)


class DeakoLightSwitch(LightEntity):
"""Deako LightEntity class."""

client: Deako
uuid: str

def __init__(self, client: Deako, uuid: str) -> None:
"""Save connection reference."""
self.client = client
self.uuid = uuid
self.client.set_state_callback(self.uuid, self.on_update)

def on_update(self) -> None:
"""State update callback."""
self.schedule_update_ha_state()

Check warning on line 50 in homeassistant/components/deako/light.py

View check run for this annotation

Codecov / codecov/patch

homeassistant/components/deako/light.py#L50

Added line #L50 was not covered by tests

@property
def device_info(self) -> DeviceInfo:
"""Returns device info in HA digestable format."""
return DeviceInfo(
identifiers={(DOMAIN, self.uuid)},
name=self.name,
manufacturer="Deako",
model="dimmer"
if ColorMode.BRIGHTNESS in self.supported_color_modes
else "smart",
)

@property
def unique_id(self) -> str:
"""Return the ID of this Deako light."""
return self.uuid

@property
def name(self) -> str:
"""Return the name of the Deako light."""
name = self.client.get_name(self.uuid)
return name or f"Unknown device: {self.uuid}"

@property
def is_on(self) -> bool:
"""Return true if the light is on."""
state = self.client.get_state(self.uuid)
if state is not None:
power = state.get("power", False)
if isinstance(power, bool):
return power
return False

Check warning on line 83 in homeassistant/components/deako/light.py

View check run for this annotation

Codecov / codecov/patch

homeassistant/components/deako/light.py#L83

Added line #L83 was not covered by tests

@property
def brightness(self) -> int:
"""Return the brightness of this light between 0..255."""
state = self.client.get_state(self.uuid)
if state is not None:
return int(round(state.get("dim", 0) * 2.55))
return 0

Check warning on line 91 in homeassistant/components/deako/light.py

View check run for this annotation

Codecov / codecov/patch

homeassistant/components/deako/light.py#L91

Added line #L91 was not covered by tests

@property
def supported_color_modes(self) -> set[ColorMode]:
"""Flag supported features."""
color_modes: set[ColorMode] = set()
state = self.client.get_state(self.uuid)
if state is not None and state.get("dim") is None:
color_modes.add(ColorMode.ONOFF)
else:
color_modes.add(ColorMode.BRIGHTNESS)
return color_modes

async def async_turn_on(self, **kwargs: Any) -> None:
"""Turn on the light."""
dim = None
if ATTR_BRIGHTNESS in kwargs:
dim = round(kwargs[ATTR_BRIGHTNESS] / 2.55, 0)
await self.client.control_device(self.uuid, True, dim)

async def async_turn_off(self, **kwargs: Any) -> None:
"""Turn off the device."""
dim = None
if ATTR_BRIGHTNESS in kwargs:
dim = round(kwargs[ATTR_BRIGHTNESS] / 2.55, 0)

Check warning on line 115 in homeassistant/components/deako/light.py

View check run for this annotation

Codecov / codecov/patch

homeassistant/components/deako/light.py#L115

Added line #L115 was not covered by tests
await self.client.control_device(self.uuid, False, dim)
12 changes: 12 additions & 0 deletions homeassistant/components/deako/manifest.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
{
"domain": "deako",
"name": "Deako Smart Lighting",
"codeowners": ["@sebirdman", "@balake", "@deakolights"],
"config_flow": true,
"dependencies": ["zeroconf"],
"documentation": "https://www.home-assistant.io/integrations/deako",
"iot_class": "local_polling",
"loggers": ["pydeako"],
"requirements": ["pydeako==0.3.2"],
"zeroconf": ["_deako._tcp.local."]
}
13 changes: 13 additions & 0 deletions homeassistant/components/deako/strings.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
{
"config": {
"step": {
"confirm": {
"description": "[%key:common::config_flow::description::confirm_setup%]"
}
},
"abort": {
"single_instance_allowed": "[%key:common::config_flow::abort::single_instance_allowed%]",
"no_devices_found": "[%key:common::config_flow::abort::no_devices_found%]"
}
}
}
1 change: 1 addition & 0 deletions homeassistant/generated/config_flows.py
Original file line number Diff line number Diff line change
Expand Up @@ -90,6 +90,7 @@
"cpuspeed",
"crownstone",
"daikin",
"deako",
"deconz",
"deluge",
"denonavr",
Expand Down
6 changes: 6 additions & 0 deletions homeassistant/generated/integrations.json
Original file line number Diff line number Diff line change
Expand Up @@ -998,6 +998,12 @@
"config_flow": false,
"iot_class": "local_polling"
},
"deako": {
"name": "Deako Smart Lighting",
"integration_type": "hub",
"config_flow": true,
"iot_class": "local_polling"
},
"debugpy": {
"name": "Remote Python Debugger",
"integration_type": "service",
Expand Down
5 changes: 5 additions & 0 deletions homeassistant/generated/zeroconf.py
Original file line number Diff line number Diff line change
Expand Up @@ -364,6 +364,11 @@
"domain": "forked_daapd",
},
],
"_deako._tcp.local.": [
{
"domain": "deako",
},
],
"_dkapi._tcp.local.": [
{
"domain": "daikin",
Expand Down
10 changes: 10 additions & 0 deletions mypy.ini
Original file line number Diff line number Diff line change
Expand Up @@ -700,6 +700,16 @@ disallow_untyped_defs = true
warn_return_any = true
warn_unreachable = true

[mypy-homeassistant.components.deako.*]
check_untyped_defs = true
disallow_incomplete_defs = true
disallow_subclassing_any = true
disallow_untyped_calls = true
disallow_untyped_decorators = true
disallow_untyped_defs = true
warn_return_any = true
warn_unreachable = true

[mypy-homeassistant.components.deconz.*]
check_untyped_defs = true
disallow_incomplete_defs = true
Expand Down
3 changes: 3 additions & 0 deletions requirements_all.txt
Original file line number Diff line number Diff line change
Expand Up @@ -1655,6 +1655,9 @@ pydaikin==2.11.1
# homeassistant.components.danfoss_air
pydanfossair==0.1.0

# homeassistant.components.deako
pydeako==0.3.2

# homeassistant.components.deconz
pydeconz==113

Expand Down
3 changes: 3 additions & 0 deletions requirements_test_all.txt
Original file line number Diff line number Diff line change
Expand Up @@ -1249,6 +1249,9 @@ pycsspeechtts==1.0.8
# homeassistant.components.daikin
pydaikin==2.11.1

# homeassistant.components.deako
pydeako==0.3.2

# homeassistant.components.deconz
pydeconz==113

Expand Down
1 change: 1 addition & 0 deletions tests/components/deako/__init__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
"""Tests for the Deako integration."""
Loading

0 comments on commit 64c264f

Please sign in to comment.