Skip to content

Commit

Permalink
Add support for aggregated grouped_lights commands (home-assistant-li…
Browse files Browse the repository at this point in the history
  • Loading branch information
marcelveldt authored Mar 22, 2022
1 parent f6d77fa commit 9660cfb
Show file tree
Hide file tree
Showing 8 changed files with 140 additions and 7 deletions.
4 changes: 4 additions & 0 deletions aiohue/errors.py
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,10 @@ class BridgeBusy(AiohueException):
"""Raised when multiple requests to the bridge failed."""


class BridgeSoftwareOutdated(AiohueException):
"""Raised when the software version of the bridge is (too) outdated."""


ERRORS = {1: Unauthorized, 101: LinkButtonNotPressed}


Expand Down
16 changes: 16 additions & 0 deletions aiohue/v2/controllers/config.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,9 @@
"""Controller holding and managing HUE resources that are of the config type."""
from typing import TYPE_CHECKING, Optional, Type, Union

from awesomeversion import AwesomeVersion

from ...errors import BridgeSoftwareOutdated
from ...util import mac_from_bridge_id
from ..models.bridge import Bridge
from ..models.bridge_home import BridgeHome
Expand Down Expand Up @@ -112,6 +115,19 @@ def bridge_device(self) -> Device:
return device
raise AttributeError("bridge_device")

def check_version(self, version: str) -> bool:
"""Check if bridge version is equal to (or higher than) given version."""
current = AwesomeVersion(self.software_version)
required = AwesomeVersion(version)
return current >= required

def require_version(self, version: str) -> None:
"""Raise exception if Bridge version is lower than given minimal version."""
if not self.require_version(version):
raise BridgeSoftwareOutdated(
f"Bridge software version outdated. Minimal required version is {version}"
)

def __init__(self, bridge: "HueBridgeV2") -> None:
"""Initialize underlying controller instances."""
self.bridges = BridgeController(bridge)
Expand Down
9 changes: 3 additions & 6 deletions aiohue/v2/controllers/groups.py
Original file line number Diff line number Diff line change
Expand Up @@ -86,12 +86,9 @@ def get_lights(self, id: str) -> List[Light]:
return []

async def set_state(self, id: str, on: bool = True) -> None:
"""
Set supported feature(s) to grouped_light resource.
NOTE: a grouped_light can only handle OnFeature
To send other features, you'll have to control the underlying lights
"""
"""Set supported feature(s) to grouped_light resource."""
# Sending (color) commands to grouped_light was added in Bridge version 1.50.1950111030
self._bridge.config.require_version("1.50.1950111030")
await self.update(id, GroupedLightPut(on=OnFeature(on=on)))


Expand Down
86 changes: 86 additions & 0 deletions aiohue/v2/models/feature.py
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,42 @@ class DimmingFeaturePut(DimmingFeatureBase):
"""Represent `Dimming` Feature when updating/sending in PUT requests."""


class DeltaAction(Enum):
"""Enum with delta actions for DimmingDelta and ColorDelta feature."""

UP = "up"
DOWN = "down"
STOP = "stop"


@dataclass
class DimmingDeltaFeaturePut:
"""
Represent `DimmingDelta` Feature when updating/sending in PUT requests.
Brightness percentage of full-scale increase delta to current dimlevel. Clip at Max-level or Min-level.
https://developers.meethue.com/develop/hue-api-v2/api-reference/#resource_light__id__put
"""

action: DeltaAction
brightness_delta: Optional[float] = None


@dataclass
class ColorTemperatureDeltaFeaturePut:
"""
Represent `DimmingDelta` Feature when updating/sending in PUT requests.
Mirek delta to current mirek. Clip at mirek_minimum and mirek_maximum of mirek_schema.
https://developers.meethue.com/develop/hue-api-v2/api-reference/#resource_light__id__put
"""

action: DeltaAction
mirek_delta: Optional[int] = None


@dataclass
class Position:
"""
Expand Down Expand Up @@ -281,6 +317,56 @@ class EffectsFeaturePut:
effect: EffectStatus


class TimedEffectStatus(Enum):
"""Enum with possible timed effects."""

NO_EFFECT = "no_effect"
SUNRISE = "sunrise"
UNKNOWN = "unknown"

@classmethod
def _missing_(cls: Type, value: str):
"""Set default enum member if an unknown value is provided."""
return AlertEffectType.UNKNOWN


@dataclass
class TimedEffectsFeature:
"""
Represent `TimedEffectsFeature` object as used by various Hue resources.
Basic feature containing timed effect properties.
https://developers.meethue.com/develop/hue-api-v2/api-reference/#resource_light_get
"""

status: EffectStatus
status_values: List[EffectStatus] = field(default_factory=list)
# Duration is mandatory when timed effect is set except for no_effect.
# Resolution decreases for a larger duration. e.g Effects with duration smaller
# than a minute will be rounded to a resolution of 1s, while effects with duration
# larger than an hour will be arounded up to a resolution of 300s.
# Duration has a max of 21600000 ms.
duration: Optional[int] = None


@dataclass
class TimedEffectsFeaturePut:
"""
Represent `TimedEffectsFeature` object when sent to the API in PUT requests.
https://developers.meethue.com/develop/hue-api-v2/api-reference/#resource_light__id__put
"""

effect: Optional[TimedEffectStatus]
# Duration is mandatory when timed effect is set except for no_effect.
# Resolution decreases for a larger duration. e.g Effects with duration smaller
# than a minute will be rounded to a resolution of 1s, while effects with duration
# larger than an hour will be arounded up to a resolution of 300s.
# Duration has a max of 21600000 ms.
duration: Optional[int] = None


class RecallAction(Enum):
"""Enum with available recall actions."""

Expand Down
18 changes: 17 additions & 1 deletion aiohue/v2/models/grouped_light.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,17 @@
from dataclasses import dataclass
from typing import Optional

from .feature import AlertFeature, AlertFeaturePut, OnFeature
from .feature import (
AlertFeature,
AlertFeaturePut,
ColorFeatureBase,
ColorTemperatureDeltaFeaturePut,
ColorTemperatureFeatureBase,
DimmingDeltaFeaturePut,
DimmingFeatureBase,
DynamicsFeaturePut,
OnFeature,
)
from .resource import ResourceTypes


Expand Down Expand Up @@ -34,4 +44,10 @@ class GroupedLightPut:
"""

on: Optional[OnFeature] = None
dimming: Optional[DimmingFeatureBase] = None
dimming_delta: Optional[DimmingDeltaFeaturePut] = None
color_temperature: Optional[ColorTemperatureFeatureBase] = None
color_temperature_delta: Optional[ColorTemperatureDeltaFeaturePut] = None
color: Optional[ColorFeatureBase] = None
dynamics: Optional[DynamicsFeaturePut] = None
alert: Optional[AlertFeaturePut] = None
8 changes: 8 additions & 0 deletions aiohue/v2/models/light.py
Original file line number Diff line number Diff line change
Expand Up @@ -12,8 +12,10 @@
AlertFeaturePut,
ColorFeature,
ColorFeatureBase,
ColorTemperatureDeltaFeaturePut,
ColorTemperatureFeature,
ColorTemperatureFeatureBase,
DimmingDeltaFeaturePut,
DimmingFeature,
DimmingFeatureBase,
DynamicsFeature,
Expand All @@ -24,6 +26,8 @@
GradientFeature,
GradientFeatureBase,
OnFeature,
TimedEffectsFeature,
TimedEffectsFeaturePut,
)
from .resource import ResourceIdentifier, ResourceTypes

Expand Down Expand Up @@ -74,6 +78,7 @@ class Light:
alert: Optional[AlertFeature] = None
gradient: Optional[GradientFeature] = None
effects: Optional[EffectsFeature] = None
timed_effects: Optional[TimedEffectsFeature] = None

type: ResourceTypes = ResourceTypes.LIGHT

Expand Down Expand Up @@ -136,9 +141,12 @@ class LightPut:

on: Optional[OnFeature] = None
dimming: Optional[DimmingFeatureBase] = None
dimming_delta: Optional[DimmingDeltaFeaturePut] = None
color_temperature: Optional[ColorTemperatureFeatureBase] = None
color_temperature_delta: Optional[ColorTemperatureDeltaFeaturePut] = None
color: Optional[ColorFeatureBase] = None
dynamics: Optional[DynamicsFeaturePut] = None
alert: Optional[AlertFeaturePut] = None
gradient: Optional[GradientFeatureBase] = None
effects: Optional[EffectsFeaturePut] = None
timed_effects: Optional[TimedEffectsFeaturePut] = None
1 change: 1 addition & 0 deletions requirements.txt
Original file line number Diff line number Diff line change
@@ -1,2 +1,3 @@
aiohttp
asyncio-throttle
awesomeversion
5 changes: 5 additions & 0 deletions tests/v2/test_init.py
Original file line number Diff line number Diff line change
Expand Up @@ -23,3 +23,8 @@ async def test_bridge_init(v2_resources):
assert bridge.scenes is not None
assert bridge.sensors is not None
assert bridge.groups is not None

# test required version check
assert bridge.config.check_version("1.50.1950111030") is False
assert bridge.config.check_version("1.48.1948086000") is True
assert bridge.config.check_version("1.48.1948085000") is True

0 comments on commit 9660cfb

Please sign in to comment.