Skip to content

Commit

Permalink
feat(controller-types)
Browse files Browse the repository at this point in the history
Each controller type has its own set of actions
  • Loading branch information
xaviml committed Feb 3, 2020
1 parent 242ea89 commit 1bc5751
Show file tree
Hide file tree
Showing 15 changed files with 512 additions and 359 deletions.
42 changes: 42 additions & 0 deletions apps/controllerx/const.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
class Light:
ON = "on"
OFF = "off"
TOGGLE = "toggle"
RELEASE = "release"
ON_FULL_BRIGHTNESS = "on_full_brightness"
ON_FULL_COLOR_TEMP = "on_full_color_temp"
CLICK_BRIGHTNESS_UP = "click_brightness_up"
CLICK_BRIGHTNESS_DOWN = "click_brightness_down"
CLICK_BRIGHTNESS_TOGGLE = "click_brightness_toggle"
CLICK_COLOR_UP = "click_color_up"
CLICK_COLOR_DOWN = "click_color_down"
CLICK_COLOR_TOGGLE = "click_color_toggle"
CLICK_COLOR_TEMP_UP = "click_color_temp_up"
CLICK_COLOR_TEMP_DOWN = "click_color_temp_down"
CLICK_COLOR_TEMP_TOGGLE = "click_color_temp_toggle"
CLICK_XY_COLOR_UP = "click_xy_color_up"
CLICK_XY_COLOR_DOWN = "click_xy_color_down"
CLICK_XY_COLOR_TOGGLE = "click_xy_color_toggle"
HOLD_BRIGHTNESS_UP = "hold_brightness_up"
HOLD_BRIGHTNESS_DOWN = "hold_brightness_down"
HOLD_BRIGHTNESS_TOGGLE = "hold_brightness_toggle"
HOLD_COLOR_UP = "hold_color_up"
HOLD_COLOR_DOWN = "hold_color_down"
HOLD_COLOR_TOGGLE = "hold_color_toggle"
HOLD_COLOR_TEMP_UP = "hold_color_temp_up"
HOLD_COLOR_TEMP_DOWN = "hold_color_temp_down"
HOLD_COLOR_TEMP_TOGGLE = "hold_color_temp_toggle"
HOLD_XY_COLOR_UP = "hold_xy_color_up"
HOLD_XY_COLOR_DOWN = "hold_xy_color_down"
HOLD_XY_COLOR_TOGGLE = "hold_xy_color_toggle"


class MediaPlayer:
HOLD_DOWN = "hold_down"
HOLD_UP = "hold_up"
VOLUME_DOWN = "volume_down"
VOLUME_UP = "volume_up"
RELEASE = "release"
PLAY_PAUSE = "play_pause"
NEXT_TRACK = "next_track"
PREVIOUS_TRACK = "previous_track"
28 changes: 18 additions & 10 deletions apps/controllerx/core/controller.py
Original file line number Diff line number Diff line change
Expand Up @@ -33,8 +33,6 @@ async def _action_impl(self, *args, **kwargs):
class Controller(hass.Hass, abc.ABC):
"""
This is the parent Controller, all controllers must extend from this class.
It is mandatory to implement `get_z2m_actions_mapping` to map the controller
actions to the internal functions.
"""

action_times = defaultdict(lambda: 0)
Expand All @@ -46,16 +44,22 @@ def initialize(self):
self.controllers_ids = self.get_list(self.args["controller"])
integration = self.get_integration(self.args["integration"])
self.actions_mapping = self.get_actions_mapping(integration)
type_actions_mapping = self.get_type_actions_mapping()
included_actions = self.get_list(
self.args.get("actions", list(self.actions_mapping.keys()))
self.args.get("actions", list(self.actions_mapping.values()))
)
self.action_delta = self.args.get("action_delta", DEFAULT_ACTION_DELTA)

# Filter the actions
self.actions_mapping = {
key: value
for key, value in self.actions_mapping.items()
if key in included_actions
if value in included_actions
}

# Map the actions mapping with the real functions
self.actions_mapping = {
k: type_actions_mapping[v] for k, v in self.actions_mapping.items()
}

for controller_id in self.controllers_ids:
Expand Down Expand Up @@ -121,6 +125,13 @@ def get_action(self, action_value):
"The action value from the action mapping should be a list or a function"
)

async def get_entity_state(self, entity, attribute=None):
if "group." in entity:
entities = await self.get_state(entity, attribute="entity_id")
entity = entities[0]
out = await self.get_state(entity, attribute=attribute)
return out

def get_z2m_actions_mapping(self):
"""
Controllers can implement this function. It should return a dict
Expand All @@ -145,12 +156,9 @@ def get_zha_actions_mapping(self):
"""
return None

async def get_entity_state(self, entity, attribute=None):
if "group." in entity:
entities = await self.get_state(entity, attribute="entity_id")
entity = entities[0]
out = await self.get_state(entity, attribute=attribute)
return out
@abc.abstractmethod
def get_type_actions_mapping(self):
pass


class ReleaseHoldController(Controller, abc.ABC):
Expand Down
Empty file.
171 changes: 158 additions & 13 deletions apps/controllerx/core/type/light_controller.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
from core.stepper import Stepper
from core.stepper.minmax_stepper import MinMaxStepper
from core.stepper.circular_stepper import CircularStepper
from const import Light
from collections import defaultdict

DEFAULT_MANUAL_STEPS = 10
Expand All @@ -27,7 +28,10 @@ class LightController(ReleaseHoldController):
"""

ATTRIBUTE_BRIGHTNESS = "brightness"
# With the following attribute, it will select color_temp or xy_color, depending on the light.
ATTRIBUTE_COLOR = "color"
ATTRIBUTE_COLOR_TEMP = "color_temp"
ATTRIBUTE_XY_COLOR = "xy_color"

# These are the 24 colors that appear in the circle color of home assistant
colors = [
Expand Down Expand Up @@ -68,19 +72,159 @@ def initialize(self):
automatic_steps = self.args.get("automatic_steps", DEFAULT_AUTOMATIC_STEPS)
color_stepper = CircularStepper(0, len(self.colors) - 1, len(self.colors))
self.manual_steppers = {
"brightness": MinMaxStepper(1, 255, manual_steps),
"color_temp": MinMaxStepper(153, 500, manual_steps),
"xy_color": color_stepper,
LightController.ATTRIBUTE_BRIGHTNESS: MinMaxStepper(1, 255, manual_steps),
LightController.ATTRIBUTE_COLOR_TEMP: MinMaxStepper(153, 500, manual_steps),
LightController.ATTRIBUTE_XY_COLOR: color_stepper,
}
self.automatic_steppers = {
"brightness": MinMaxStepper(1, 255, automatic_steps),
"color_temp": MinMaxStepper(153, 500, automatic_steps),
"xy_color": color_stepper,
LightController.ATTRIBUTE_BRIGHTNESS: MinMaxStepper(
1, 255, automatic_steps
),
LightController.ATTRIBUTE_COLOR_TEMP: MinMaxStepper(
153, 500, automatic_steps
),
LightController.ATTRIBUTE_XY_COLOR: color_stepper,
}
self.smooth_power_on = self.args.get(
"smooth_power_on", self.supports_smooth_power_on()
)

def get_type_actions_mapping(self):
return {
Light.ON: self.on,
Light.OFF: self.off,
Light.TOGGLE: self.toggle,
Light.RELEASE: self.release,
Light.ON_FULL_BRIGHTNESS: (
self.on_full,
LightController.ATTRIBUTE_BRIGHTNESS,
),
Light.ON_FULL_COLOR_TEMP: (
self.on_full,
LightController.ATTRIBUTE_COLOR_TEMP,
),
Light.CLICK_BRIGHTNESS_UP: (
self.click,
LightController.ATTRIBUTE_BRIGHTNESS,
Stepper.UP,
),
Light.CLICK_BRIGHTNESS_DOWN: (
self.click,
LightController.ATTRIBUTE_BRIGHTNESS,
Stepper.DOWN,
),
Light.CLICK_BRIGHTNESS_TOGGLE: (
self.click,
LightController.ATTRIBUTE_BRIGHTNESS,
Stepper.TOGGLE,
),
Light.CLICK_COLOR_UP: (
self.click,
LightController.ATTRIBUTE_COLOR,
Stepper.UP,
),
Light.CLICK_COLOR_DOWN: (
self.click,
LightController.ATTRIBUTE_COLOR,
Stepper.DOWN,
),
Light.CLICK_COLOR_TOGGLE: (
self.click,
LightController.ATTRIBUTE_COLOR,
Stepper.TOGGLE,
),
Light.CLICK_COLOR_TEMP_UP: (
self.click,
LightController.ATTRIBUTE_COLOR_TEMP,
Stepper.UP,
),
Light.CLICK_COLOR_TEMP_DOWN: (
self.click,
LightController.ATTRIBUTE_COLOR_TEMP,
Stepper.DOWN,
),
Light.CLICK_COLOR_TEMP_TOGGLE: (
self.click,
LightController.ATTRIBUTE_COLOR_TEMP,
Stepper.TOGGLE,
),
Light.CLICK_XY_COLOR_UP: (
self.click,
LightController.ATTRIBUTE_XY_COLOR,
Stepper.UP,
),
Light.CLICK_XY_COLOR_DOWN: (
self.click,
LightController.ATTRIBUTE_XY_COLOR,
Stepper.DOWN,
),
Light.CLICK_XY_COLOR_TOGGLE: (
self.click,
LightController.ATTRIBUTE_XY_COLOR,
Stepper.TOGGLE,
),
Light.HOLD_BRIGHTNESS_UP: (
self.hold,
LightController.ATTRIBUTE_BRIGHTNESS,
Stepper.UP,
),
Light.HOLD_BRIGHTNESS_DOWN: (
self.hold,
LightController.ATTRIBUTE_BRIGHTNESS,
Stepper.DOWN,
),
Light.HOLD_BRIGHTNESS_TOGGLE: (
self.hold,
LightController.ATTRIBUTE_BRIGHTNESS,
Stepper.TOGGLE,
),
Light.HOLD_COLOR_UP: (
self.hold,
LightController.ATTRIBUTE_COLOR,
Stepper.UP,
),
Light.HOLD_COLOR_DOWN: (
self.hold,
LightController.ATTRIBUTE_COLOR,
Stepper.DOWN,
),
Light.HOLD_COLOR_TOGGLE: (
self.hold,
LightController.ATTRIBUTE_COLOR,
Stepper.TOGGLE,
),
Light.HOLD_COLOR_TEMP_UP: (
self.hold,
LightController.ATTRIBUTE_COLOR_TEMP,
Stepper.UP,
),
Light.HOLD_COLOR_TEMP_DOWN: (
self.hold,
LightController.ATTRIBUTE_COLOR_TEMP,
Stepper.DOWN,
),
Light.HOLD_COLOR_TEMP_TOGGLE: (
self.hold,
LightController.ATTRIBUTE_COLOR_TEMP,
Stepper.TOGGLE,
),
Light.HOLD_XY_COLOR_UP: (
self.hold,
LightController.ATTRIBUTE_XY_COLOR,
Stepper.UP,
),
Light.HOLD_XY_COLOR_DOWN: (
self.hold,
LightController.ATTRIBUTE_XY_COLOR,
Stepper.DOWN,
),
Light.HOLD_XY_COLOR_TOGGLE: (
self.hold,
LightController.ATTRIBUTE_XY_COLOR,
Stepper.TOGGLE,
),
}

def get_light(self, light):
type_ = type(light)
if type_ == str:
Expand All @@ -107,22 +251,23 @@ async def toggle(self):

@action
async def on_full(self, attribute):
attribute = await self.get_attribute(attribute)
stepper = self.manual_steppers[attribute]
await self.change_light_state(
stepper.minmax.min, attribute, Stepper.UP, stepper,
)

async def get_attribute(self, attribute):
if attribute == self.ATTRIBUTE_COLOR:
if attribute == LightController.ATTRIBUTE_COLOR:
entity_states = await self.get_entity_state(
self.light["name"], attribute="all"
)
entity_attributes = entity_states["attributes"]
if self.light["color_mode"] == "auto":
if "xy_color" in entity_attributes:
return "xy_color"
elif "color_temp" in entity_attributes:
return "color_temp"
if LightController.ATTRIBUTE_XY_COLOR in entity_attributes:
return LightController.ATTRIBUTE_XY_COLOR
elif LightController.ATTRIBUTE_COLOR_TEMP in entity_attributes:
return LightController.ATTRIBUTE_COLOR_TEMP
else:
raise ValueError(
"This light does not support xy_color or color_temp"
Expand All @@ -133,7 +278,7 @@ async def get_attribute(self, attribute):
return attribute

async def get_value_attribute(self, attribute):
if attribute == "xy_color":
if attribute == LightController.ATTRIBUTE_XY_COLOR:
return None
else:
return await self.get_entity_state(self.light["name"], attribute)
Expand Down Expand Up @@ -185,7 +330,7 @@ async def change_light_state(self, old, attribute, direction, stepper):
value and attribute. It returns True when no more changes will need to be done.
Otherwise, it returns False.
"""
if attribute == "xy_color":
if attribute == LightController.ATTRIBUTE_XY_COLOR:
self.index_color, _ = stepper.step(self.index_color, direction)
new_state_attribute = self.colors[self.index_color]
attributes = {
Expand Down
13 changes: 13 additions & 0 deletions apps/controllerx/core/type/media_player_controller.py
Original file line number Diff line number Diff line change
@@ -1,12 +1,25 @@
from core.controller import ReleaseHoldController, action
from core.stepper import Stepper
from const import MediaPlayer


class MediaPlayerController(ReleaseHoldController):
def initialize(self):
super().initialize()
self.media_player = self.args["media_player"]

def get_type_actions_mapping(self):
return {
MediaPlayer.HOLD_DOWN: (self.hold, Stepper.DOWN),
MediaPlayer.HOLD_UP: (self.hold, Stepper.UP),
MediaPlayer.VOLUME_DOWN: self.volume_down,
MediaPlayer.VOLUME_UP: self.volume_up,
MediaPlayer.RELEASE: self.release,
MediaPlayer.PLAY_PAUSE: self.play_pause,
MediaPlayer.NEXT_TRACK: self.next_track,
MediaPlayer.PREVIOUS_TRACK: self.previous_track,
}

@action
async def play_pause(self):
self.call_service("media_player/media_play_pause", entity_id=self.media_player)
Expand Down
Loading

0 comments on commit 1bc5751

Please sign in to comment.