Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add support for smart_scene resource #170

Merged
merged 6 commits into from
Jan 2, 2023
Merged
Show file tree
Hide file tree
Changes from 3 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
63 changes: 60 additions & 3 deletions aiohue/v2/controllers/scenes.py
Original file line number Diff line number Diff line change
@@ -1,15 +1,26 @@
"""Controller holding and managing HUE resources of type `scene`."""
from typing import Optional, Type, Union
from typing import TYPE_CHECKING, Optional, Type, Union

from ..models.feature import DimmingFeaturePut, RecallAction, RecallFeature
from ..models.resource import ResourceTypes
from ..models.room import Room
from ..models.scene import Scene, ScenePut
from ..models.smart_scene import (
SmartScene,
SmartScenePut,
SmartSceneRecall,
SmartSceneRecallAction,
)
from ..models.zone import Zone
from .base import BaseResourcesController
from .base import BaseResourcesController, GroupedControllerBase

if TYPE_CHECKING:
from .. import HueBridgeV2

class ScenesController(BaseResourcesController[Type[Scene]]):
SCENE_TYPES = Union[Scene, SmartScene]


class RegularScenesController(BaseResourcesController[Type[Scene]]):
"""Controller holding and managing HUE resources of type `scene`."""

item_type = ResourceTypes.SCENE
Expand All @@ -34,3 +45,49 @@ def get_group(self, id: str) -> Union[Room, Zone]:
"""Get group attached to given scene id."""
scene = self[id]
return next((x for x in self._bridge.groups if x.id == scene.group.rid))


class SmartScenesController(BaseResourcesController[Type[SmartScene]]):
"""Controller holding and managing HUE resources of type `smart_scene`."""

item_type = ResourceTypes.SMART_SCENE
item_cls = SmartScene
allow_parser_error = True

async def recall(
self, id: str, action: SmartSceneRecallAction = SmartSceneRecallAction.ACTIVATE
) -> None:
"""Turn on / recall scene."""
update_obj = SmartScenePut(recall=SmartSceneRecall(action=action))
await self.update(id, update_obj)

def get_group(self, id: str) -> Union[Room, Zone]:
"""Get group attached to given scene id."""
scene = self[id]
return next((x for x in self._bridge.groups if x.id == scene.group.rid))


class ScenesController(GroupedControllerBase[SCENE_TYPES]):
"""Controller grouping resources of all sensor resources."""

def __init__(self, bridge: "HueBridgeV2") -> None:
"""Initialize instance."""
self.scene = RegularScenesController(bridge)
self.smart_scene = SmartScenesController(bridge)
super().__init__(
bridge,
[
self.scene,
self.smart_scene,
],
)

async def recall(self, id: str, *args, **kwargs) -> None:
marcelveldt marked this conversation as resolved.
Show resolved Hide resolved
"""Turn on / recall scene."""
scene = self[id]
# forward call to correct controller
# this method is here for convenience (and backwards compatibility)
if isinstance(scene, SmartScene):
await self.smart_scene.recall(id, *args, **kwargs)
return
await self.scene.recall(id, *args, **kwargs)
1 change: 1 addition & 0 deletions aiohue/v2/models/resource.py
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,7 @@ class ResourceTypes(Enum):
DEVICE_DISCOVERY = "device_discovery"
SYSTEM_UPDATE = "system_update"
SCENE = "scene"
SMART_SCENE = "smart_scene"
ENTERTAINMENT_CONFIGURATION = "entertainment_configuration"
PUBLIC_IMAGE = "public_image"
AUTH_V1 = "auth_v1"
Expand Down
164 changes: 164 additions & 0 deletions aiohue/v2/models/smart_scene.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,164 @@
"""
Model(s) for Smart Scene resource on HUE bridge.

https://developers.meethue.com/develop/hue-api-v2/api-reference/#resource_smart_scene
"""
from dataclasses import dataclass
from enum import Enum
from typing import List, Optional, Type

from .resource import ResourceIdentifier, ResourceTypes
from .scene import SceneMetadata, SceneMetadataPut


class WeekDay(Enum):
"""Represent Day of Week in TimeSlot."""

SUNDAY = "sunday"
MONDAY = "monday"
TUESDAY = "tuesday"
WEDNESDAY = "wednesday"
THURSDAY = "thursday"
FRIDAY = "friday"
SATURDAY = "saturday"


class SmartSceneState(Enum):
"""The state of the Active Scene."""

ACTIVE = "active"
INACTIVE = "inactive"

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


@dataclass
class TimeslotStartTimeTime:
"""Time object."""

hour: int # minimum: 0 – maximum: 23
minute: int # minimum: 0 – maximum: 59
second: int # minimum: 0 – maximum: 59

def __post_init__(self):
"""Validate values."""
if self.hour < 0 or self.hour > 23:
raise ValueError("Hour must be a value within range of 0 and 23")
if self.minute < 0 or self.minute > 59:
raise ValueError("Minute must be a value within range of 0 and 59")
if self.second < 0 or self.second > 59:
raise ValueError("Second must be a value within range of 0 and 59")


@dataclass
class TimeslotStartTime:
"""Representation of a Start time object within a timeslot."""

kind: str # currently fixed to 'time'
time: TimeslotStartTimeTime


@dataclass
class SmartSceneTimeslot:
"""
Represent SmartSceneTimeslot as used by Smart Scenes.

Information on what is the light state for every timeslot of the day.
"""

start_time: TimeslotStartTime
target: ResourceIdentifier


@dataclass
class DayTimeSlots:
"""Represent DayTimeSlots information, used by Smart Scenes."""

timeslots: List[SmartSceneTimeslot]
recurrence: List[WeekDay]


@dataclass
class SmartSceneActiveTimeslot:
"""The active time slot in execution."""

timeslot_id: int
weekday: WeekDay


class SmartSceneRecallAction:
"""
Enum with possible recall actions for smart scenes.

Activate will start the smart (24h) scene; deactivate will stop it.
"""

ACTIVATE = "activate"
DEACTIVATE = "deactivate"


@dataclass
class SmartSceneRecall:
"""Properties to send when activating a Smart Scene."""

action: SmartSceneRecallAction


@dataclass
class SmartScene:
"""
Represent (full) `SmartScene` Model when retrieved from the API.

https://developers.meethue.com/develop/hue-api-v2/api-reference/#resource_smart_scene__id__get
"""

id: str
metadata: SceneMetadata
# group: required(object)
# Group associated with this Scene. All services in the group are part of this scene.
# If the group is changed the scene is updated (e.g. light added/removed)
group: ResourceIdentifier
# actions: required(array of Action)
# List of actions to be executed synchronously on recal
week_timeslots: List[DayTimeSlots]
# active_timeslot: information on what is the light state for every timeslot of the day
active_timeslot: SmartSceneActiveTimeslot
# state: the current state of the smart scene.
# The default state is inactive if no recall is provided
state: SmartSceneState

# optional params
id_v1: Optional[str] = None

type: ResourceTypes = ResourceTypes.SMART_SCENE


@dataclass
class SmartScenePut:
"""
Properties to send when updating/setting a `SmartScene` object on the api.

https://developers.meethue.com/develop/hue-api-v2/api-reference/#resource_smart_scene__id__put
"""

metadata: Optional[SceneMetadataPut] = None
week_timeslots: Optional[List[DayTimeSlots]] = None
recall: Optional[SmartSceneRecall] = None


@dataclass
class SmartSceneScenePost:
"""
Properties to send when creating a `SmartScene` object on the api.

https://developers.meethue.com/develop/hue-api-v2/api-reference/#resource_smarft_scene_post
"""

metadata: SceneMetadata
group: ResourceIdentifier
week_timeslots: List[DayTimeSlots]
recall: Optional[SmartSceneRecall] = None
type: ResourceTypes = ResourceTypes.SMART_SCENE