Skip to content

Commit

Permalink
feat+device
Browse files Browse the repository at this point in the history
- Individual device delay
- Adding white to the list of colors
- Support for E1744 as light and media player
- Support for Philips Hue dimmer switch
  • Loading branch information
xaviml committed Jan 11, 2020
1 parent 1ecba02 commit 0c14160
Show file tree
Hide file tree
Showing 2 changed files with 141 additions and 31 deletions.
47 changes: 23 additions & 24 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,15 +2,17 @@

[![hacs_badge](https://img.shields.io/badge/HACS-Default-orange.svg?style=for-the-badge)](https://github.com/hacs/integration)

_Bring full functionality to light controllers_
_Bring full functionality to light and media player controllers_

This automation brings the following functionalities lights:
This automation brings the following functionalities for different [devices](https://github.com/xaviml/z2m_ikea_controller/wiki/Supported-controllers):

- Turn on/Turn off light(s)
- Toggle light(s)
- Manual increase/decrease of brightness and color temperature
- Smooth increase/decrease (holding button) of brightness and color temperature
- Color loop changing if the light supports xy color.
- Play/pause music
- Volume up/down for a media player.

## Installation

Expand Down Expand Up @@ -46,7 +48,7 @@ nameOfYourInstanceApp:
color_mode: auto | xy_color | color_temp
```
And this is a real example for E1524/E1810 controller that controls all the livingroom lights.
This is a real example for E1524/E1810 controller that controls all the livingroom lights.
```yaml
livingroom_controller:
Expand All @@ -56,33 +58,30 @@ livingroom_controller:
light: group.livingroom_lights
```
You can check in the wiki the [supported controller](https://github.com/xaviml/z2m_ikea_controller/wiki/Supported-controllers).
This is a real example to control a media player with E1744:
These are the app parameters:
| key | optional | type | default | example | description |
| ----------------- | -------- | -------------------- | ----------- | --------------------------------------------------------------------------------------------------------------------------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `module` | False | string | - | `z2m_ikea_controller` | The Python module |
| `class` | False | string | - | `E1810Controller` | The Python class. Check the classes for each controller on the [supported controllers](https://github.com/xaviml/z2m_ikea_controller/wiki/Supported-controllers) page. |
| `sensor` | False | string \| list | - | `sensor.livingroom_controller_action` or `sensor.livingroom_sensor.livingroom_controller_action1, sensor.livingroom_controller_action2` | The sensor(s) entity id from HA. Note that for IKEA E1524/E1810 it finishes with "\_action" by default and for IKEA E1743 with "\_click". This can be also sent as list on the YAML (using "-") |
| `light` | False | string \| dictionary | - | `group.livingroom_lights` or `light.kitchen` | The light (or group of lights) you want to control |
| `manual_steps` | True | int | 10 | | Number of steps to go from min to max when clicking. If the value is 2 with one click you will set the light to 50% and with another one to 100%. |
| `automatic_steps` | True | int | 10 | | Number of steps to go from min to max when smoothing. If the value is 2 with one click you will set the light to 50% and with another one to 100%. |
| `delay` | True | int | 350 | | Delay in milliseconds that takes between sending the instructions to the light (for the smooth functionality). Note that the maximum value is 1000 and if leaving to 0, you might get uncommon behaviour. |
| `actions` | True | list | All actions | | This is a list of actions to be included and controlled by the app. To see which actions has each controller check the [supported controllers](https://github.com/xaviml/z2m_ikea_controller/wiki/Supported-controllers) page |
```yaml
bedroom_speaker:
module: z2m_ikea_controller
class: E1744MediaPlayerController
sensor: sensor.symfonisk_controller_action
media_player: media_player.bedroom_speaker
```
Light dictionary for the `light` attribute:
These are the generic app parameters for all type of controllers. You can see the rest in [here](https://github.com/xaviml/z2m_ikea_controller/wiki/Controller-types)
| key | optional | type | default | example | description |
| ------------ | -------- | ------ | ------- | --------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `name` | False | string | - | `light.kitchen` | The light (or group of lights) you want to control |
| `color_mode` | True | string | `auto` | | This attribute can take `auto`, `xy_color` or `color_temp` as value. `auto` will check first if the light supports `xy_color` and then `color_temp`. `xy_color` will cicle through different colors infinitely. `color_temp` will change the color temperature attribute of the light. If a light supports both, user can pick which action wants for the light(s). |
| key | optional | type | default | example | description |
| --------- | -------- | -------------- | ----------- | --------------------------------------------------------------------------------------------------------------------------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `module` | False | string | - | `z2m_ikea_controller` | The Python module |
| `class` | False | string | - | `E1810Controller` | The Python class. Check the classes for each controller on the [supported controllers](https://github.com/xaviml/z2m_ikea_controller/wiki/Supported-controllers) page. |
| `sensor` | False | string \| list | - | `sensor.livingroom_controller_action` or `sensor.livingroom_sensor.livingroom_controller_action1, sensor.livingroom_controller_action2` | The sensor(s) entity id from HA. Note that for IKEA E1524/E1810 it finishes with "\_action" by default and for IKEA E1743 with "\_click". This can be also sent as list on the YAML (using "-") |
| `actions` | True | list | All actions | | This is a list of actions to be included and controlled by the app. To see which actions has each controller check the [supported controllers](https://github.com/xaviml/z2m_ikea_controller/wiki/Supported-controllers) page |

_TODO_ list:

- [x] Color support
- [ ] Give support to Hue dimmer controller
- [ ] Give support to Symfonisk controller for media_player
- [x] Give support to Hue dimmer controller
- [x] Give support to Symfonisk controller for media_player
- [ ] Change the name of the app by removing _ikea_

_Note: This was tested with Zigbee2MQTT and IKEA devices, but the code does not use any MQTT calls, just Home assistant API._
_Note: This was tested with Zigbee2MQTT, IKEA devices and the Philips Hue dimmer, but the code does not use any MQTT calls, just the Home Assistant API._
125 changes: 118 additions & 7 deletions apps/z2m_ikea_controller/z2m_ikea_controller.py
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ def _action_impl(self, *args, **kwargs):
continue_call = self.before_action(method.__name__)
if continue_call:
method(self, *args, **kwargs)

return _action_impl


Expand All @@ -42,6 +43,9 @@ class Controller(hass.Hass, abc.ABC):
actions to the internal functions.
"""

DIRECTION_UP = "up"
DIRECTION_DOWN = "down"

def initialize(self):
self.actions_mapping = self.get_actions_mapping()
included_actions = self.args.get("actions", list(self.actions_mapping.keys()))
Expand Down Expand Up @@ -97,7 +101,7 @@ def initialize(self):
super().initialize()
self.on_hold = False
# Since time.sleep is not recommended I limited to 1s
self.delay = min(1000, self.args.get("delay", DEFAULT_DELAY))
self.delay = min(1000, self.args.get("delay", self.default_delay()))

@action
def release(self):
Expand Down Expand Up @@ -127,6 +131,13 @@ def hold_loop(self):
"""
pass

def default_delay(self):
"""
This function can be overwritten for each device to indeicate the delay
for the specific device, by default it returns the default delay from the app
"""
return DEFAULT_DELAY


###############################################################
###############################################################
Expand Down Expand Up @@ -160,8 +171,6 @@ class LightController(ReleaseHoldController):

ATTRIBUTE_BRIGHTNESS = "brightness"
ATTRIBUTE_COLOR = "color"
DIRECTION_UP = "up"
DIRECTION_DOWN = "down"

# These are the 24 colors that appear in the circle color of home assistant
colors = [
Expand Down Expand Up @@ -196,7 +205,7 @@ class LightController(ReleaseHoldController):
"color_temp": {"min": 153, "max": 500},
}

sign_mapping = {DIRECTION_UP: 1, DIRECTION_DOWN: -1}
sign_mapping = {Controller.DIRECTION_UP: 1, Controller.DIRECTION_DOWN: -1}

def initialize(self):
super().initialize()
Expand Down Expand Up @@ -322,6 +331,37 @@ def change_light_state(self, old, attribute, direction, steps):
return True


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

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

@action
def previous_track(self):
self.call_service(
"media_player/media_previous_track", entity_id=self.media_player
)

@action
def next_track(self):
self.call_service("media_player/media_next_track", entity_id=self.media_player)

def hold_loop(self, direction):
if direction == Controller.DIRECTION_UP:
self.call_service("media_player/volume_up", entity_id=self.media_player)
else:
self.call_service("media_player/volume_down", entity_id=self.media_player)
return False

def default_delay(self):
return 500


###############################################################
###############################################################
### DEVICES ###
Expand Down Expand Up @@ -400,15 +440,15 @@ class ICTCG1Controller(LightController):
# rotate_right, rotate_right_quick
# rotate_stop

@action
def rotate_left_quick(self):
self.release()
self.off()

@action
def rotate_right_quick(self):
self.release()
self.on_full(
LightController.ATTRIBUTE_BRIGHTNESS
)
self.on_full(LightController.ATTRIBUTE_BRIGHTNESS)

def get_actions_mapping(self):
return {
Expand All @@ -422,3 +462,74 @@ def get_actions_mapping(self):
"rotate_right_quick": lambda: self.rotate_right_quick(),
"rotate_stop": lambda: self.release(),
}


class E1744LightController(LightController):
# Different states reported from the controller:
# rotate_left, rotate_right, rotate_stop,
# play_pause, skip_forward, skip_backward

def get_actions_mapping(self):
return {
"rotate_left": lambda: self.hold(
LightController.ATTRIBUTE_BRIGHTNESS, LightController.DIRECTION_DOWN
),
"rotate_right": lambda: self.hold(
LightController.ATTRIBUTE_BRIGHTNESS, LightController.DIRECTION_UP
),
"rotate_stop": lambda: self.release(),
"play_pause": lambda: self.toggle(),
"skip_forward": lambda: self.on_full(LightController.ATTRIBUTE_BRIGHTNESS),
}


class E1744MediaPlayerController(MediaPlayerController):
# Different states reported from the controller:
# rotate_left, rotate_right, rotate_stop,
# play_pause, skip_forward, skip_backward

def get_actions_mapping(self):
return {
"rotate_left": lambda: self.hold(LightController.DIRECTION_DOWN),
"rotate_right": lambda: self.hold(LightController.DIRECTION_UP),
"rotate_stop": lambda: self.release(),
"play_pause": lambda: self.play_pause(),
"skip_forward": lambda: self.next_track(),
"skip_backward": lambda: self.previous_track(),
}


class HueDimmerController(LightController):
# Different states reported from the controller:
# on-press, on-hold, on-hold-release, up-press, up-hold,
# up-hold-release, down-press, down-hold, down-hold-release,
# off-press, off-hold, off-hold-release

def get_actions_mapping(self):
return {
"on-press": lambda: self.on(),
"on-hold": lambda: self.hold(
LightController.ATTRIBUTE_COLOR, LightController.DIRECTION_UP
),
"on-hold-release": lambda: self.release(),
"up-press": lambda: self.click(
LightController.ATTRIBUTE_BRIGHTNESS, LightController.DIRECTION_UP
),
"up-hold": lambda: self.hold(
LightController.ATTRIBUTE_BRIGHTNESS, LightController.DIRECTION_UP
),
"up-hold-release": lambda: self.release(),
"down-press": lambda: self.click(
LightController.ATTRIBUTE_BRIGHTNESS, LightController.DIRECTION_DOWN
),
"down-hold": lambda: self.hold(
LightController.ATTRIBUTE_BRIGHTNESS, LightController.DIRECTION_DOWN
),
"down-hold-release": lambda: self.release(),
"off-press": lambda: self.off(),
"off-hold": lambda: self.hold(
LightController.ATTRIBUTE_COLOR, LightController.DIRECTION_DOWN
),
"off-hold-release": lambda: self.release(),
}

0 comments on commit 0c14160

Please sign in to comment.