xaviml / controllerx

Create controller-based automations with ease to control your home devices and scenes.
https://xaviml.github.io/controllerx/
MIT License
336 stars 69 forks source link

Volume up/down - steps to small with Sonos speakers #27

Closed htvekov closed 4 years ago

htvekov commented 4 years ago

Info: HA range for Sonos volume: 0.00 to 1.00 (with 2 decimals). HA direct service call also adjust volume with same amount (0.02) as ControllerX.

AppDaemon: 4 Hass-core: 0.105.2 Device: Ikea remote E1810 / Sonos One/Play:5/Play:1 speakers. Only tested with single Sonos speakers (no groups) Integration: z2m Logs: None (No errors. Only slow volume adjusting ๐Ÿ˜‰)

xaviml commented 4 years ago

Hi @htvekov

I released v2.2.1b1 with a fix for this bug. These are the instructions to try the code:

Through HACS

If you go to ControllerX in HACS, you can select in the menu Show Beta and install v2.2.1b1. Then you will need to restart your AppDaemon server.

Through files

You can download the dev branch and copy the controllerx folder inside apps into /config/appdaemon/apps/. Then you will need to restart your AppDaemon server.

There is a new attribute for media player controllers called "volume_steps" which is 10 by default. This value is how many steps need to be made to go from min to max. So if set to "2" and the volume is 0, it will go to 50% and with the second click to 100%.

Let me know if this fixes the issue and it works as expected :) If you guys try it out, let me know about it. Thanks :)

htvekov commented 4 years ago

Hi' Xavi.

Just tested the beta and volume is now working perfectly with Sonos speakers ๐Ÿ‘๐Ÿ˜ Volume steps at appx. 20 is in my opinion a good setting for Sonos. Not to fast and not to slow.

I must admit that I'm quite impressed with your app and your dedication to make it better and more versatile ๐Ÿ˜Ž

One thing that could improve your app even further, is to be able to switch between Sonos sources which are stored in Sonos app and exposed to HA (Spotify playlists, Radio stations, Pod cast etc.) For now when your not playing a playlist, left/right button does nothing/no action on Sonos speakers. Here's the attributes for one of my Sonos speakers with the source list exposed: image

With service call media_player.select_source, source can be set and will be started with same call.

image

Thanks for all the work so far, Xavi. I really appreciate it !!!

Cheers Henning

xaviml commented 4 years ago

Hi @htvekov,

Thanks for the feedback. I am glad that now works as expected and thanks for the appreciation.

I am currently working on making a custom controller where you will be able to set the custom call services on controller actions through the yaml configuration (#12). With this, you will be able to map any action to a service (e.g. change the source for you media player). I will keep you updated on it.

I will close this issue once I release v2.2.1.

htvekov commented 4 years ago

Hi' Xavi.

Custom controller - I like it already !! ๐Ÿ˜Ž๐Ÿ‘

xaviml commented 4 years ago

This issue has been fixed in v2.2.1

xaviml commented 4 years ago

Hi @htvekov ,

I have released v2.3.0b1 with support to custom controllers, now you can map controller events to predefined actions or HA services. Since there is no support for sources list yet on the media player controllers, you can define this with CallServiceController. This would be a configuration that let you control your media player and also adds the functionality for the source list.

office_speaker:
  module: controllerx
  class: E1810MediaPlayerController
  controller: sensor.office_controller_action
  integration: z2m
  media_player: media_player.office_speaker
  actions:
    - toggle
    - brightness_up_click
    - brightness_down_click
    - arrow_left_click
    - arrow_right_click
    - brightness_up_hold
    - brightness_up_release
    - brightness_down_hold
    - brightness_down_release
office_speaker_source_list:
  module: controllerx
  class: CallServiceController
  controller: sensor.office_controller_action
  integration: z2m
  mapping:
    arrow_left_hold:
      service: media_player.select_source
      data:
        entity_id: media_player.office_speaker
        source: DR P5 (Pop)
    arrow_right_hold:
      service: media_player.select_source
      data:
        entity_id: media_player.office_speaker
        source: DR P6 Beat

The actions attribute from the first app instance is not necessary, but ust in case in the future we are a functionality to the 2 events that are not used (arrow_left_hold and arrow_right_hold).

If you hold the arrow left it should change to DR P5 (Pop) and if you hold the right arrow it should change to DR P6 Beat. I will try to add a predefined action to change source list on every click or hold (from left to right in a circular way). But this won't be added yet.

Let me know if this works as expected. Thanks @htvekov :)

htvekov commented 4 years ago

Hi' Xavi.

Didn't even realise (until now), that you've also written on this issue. Do you sleep at all ? ๐Ÿ˜‰

Thank you for the example ๐Ÿ‘ I'll test in the weekend. This is better than nothing. But you're right. I would really like to be able to 'scroll' forward and backwards in the source list. As automation this will only be possible AFAIK with heavy scripting and complex string compare commands/syntax). Automation really are limited with more complex situations. I need variables and the possibility to store strings etc. It's quite difficult to do without, since all programming I've done since the 80's have included this. I'm 'old school' and not used to YAML syntax, Linux, Python etc. ๐Ÿ˜But I'm not dead yet and learning every day ๐Ÿ˜‰But the 'learning curve' is unfirtunately not as steep as 20 years ago.

Thank you once again, Xavi. Really appreciated !

Regards Henning

xaviml commented 4 years ago

Perfect @htvekov! Let me know if this works as expected then and if you have any problems.

I will try to implement a predefined action for the forward/backward source list, but I do no promise anything. Can you maybe let me know if source list is also available when the speaker is off or idle?

I agree with you, HA automations are limited when it comes to complex situations, but this is why solutions like python script and AppDaemon exist, which I am glad for it. If you ever need help or you have any question with Python, you can ask me :)

Regards, Xavi M.

htvekov commented 4 years ago

Hi' Xavi.

I'll test your sample code tomorrow. Don't expect any problems, it's quite straighforward ๐Ÿ˜Š

But it would be REALLY cool if you at some point could find the time to implement Sonos source list choice ๐Ÿ˜Ž๐Ÿ˜Ž

Just made a quick test with my Sonos speakers. When paused/idle source list is available. And luckily source list is also available in HA, even if speaker is not available (offline completely) ๐Ÿ˜Š Source list is dynamic. If changes are made via Sonos app to 'favourites list', this will immediately reflect on states in HA for all (online) speakers.

If speaker is completely off, the change to source list will be applied when online again (as idle/paused). Didn't notice that source list was actually updated before I was using it for playing. But maybe I wasn't patient enough for states to update ?

image

Wish you a lovely weekend, Xavi ๐Ÿป๐Ÿป๐Ÿ˜

htvekov commented 4 years ago

Just tested, Xavi.

Test example for source list is working as expected ๐Ÿ˜Š๐Ÿ‘ Only one service call pr. mapped key allowed, or should syntax be altered ? Right now only last service call is executed, if more than one is listed (pr. key) I would like to short flash a light, to indicate key actually has been pressed. As it takes Sonos some 5-6 seconds to initiate new source list and play.

image

xaviml commented 4 years ago

Hi @htvekov

I have some good news for you, Santa came late, but it came. I have implemented the following in v2.3.0b5:

  1. Predefined action in media players: Go next and previous source. Now on the custom controllers you can call the actions next_source and previous_source. This is a simple example that JUST changes the source when left and right arrows are clicked.
    example_app:
    module: controllerx
    class: CustomMediaPlayerController
    controller: livingroom-controller
    integration: z2m
    media_player: media_player.livingroom_speaker
    mapping:
    arrow_left_click: previous_source
    arrow_right_click: next_source

    Also, I added to the E1810MediaPlayerController the next and previous source when holding arrow left and right. So just with the following configuration, you have the next and previous source:

    office_controller:
    module: controllerx
    class: E1810MediaPlayerController
    controller: <sensor id>
    integration: z2m
    volume_steps: 20
    media_player: group.sonos_all
  2. Call multiple call services. In the previous comment you said that you wanted to call two services and unfortunately that was not implemented, but since Santa came, I have added support for multiple services as well as just one service (like before). However, when you want to state a list in yaml, you need to use the dash "-", otherwise it is parsed as a map, so your configuration should look like:
    office_controller:
    module: controllerx
    class: E1810MediaPlayerController
    controller: <sensor id>
    integration: z2m
    volume_steps: 20
    media_player: group.sonos_all
    actions:
    - toggle
    - brightness_up_click
    - brightness_down_click
    #- arrow_left_click
    #- arrow_right_click
    - brightness_up_hold
    - brightness_up_release
    - brightness_down_hold
    - brightness_down_release
    office_speaker_source_list:
    module: controllerx
    class: CallServiceController
    controller: <sensor id>
    integration: z2m
    mapping: 
    arrow_left_click: 
      - service: media_player.select_source
        data:
          entity_id: group.sonos_all
          source: DR P3
      - service: light.turn_on
        data:
          entity_id: light.kontor_loft
          flash: short
    arrow_right_click:
      - service: media_player.select_source
        data:
          entity_id: group.sonos_all
          source: DR P4 Fyn 96.8 (Nyheder)
      - service: light.turn_on
        data:
          entity_id: light.kontor_loft
          flash: short

One last thing, since the next and previous source is a predefined action, you cannot call a service (like turning on a light) after next and previous has been called since they are different custom controllers. However, you can turn the light at the same time as the source is changing by creating two app instances as you have done and then mapping in the same key two different actions but with different type of custom controller.

I would really appreciate it if you could test both (number 1 and 2) configurations to know that it works as expected in your end. I have implemented it and I put everything under tests, but I do not have a speaker with a source list to test. Thank you and merry Christmas :stuck_out_tongue_closed_eyes:

Note: I have converted the image to text with an OCR online, there might be some typos. Just for future reference, you can check here how to include code in markdown. Instead of "javascript" or "python", you can put "yaml".

htvekov commented 4 years ago

Hi' Xavi

Holy crap - My saviour in shining Python armor ! ๐Ÿš€๐Ÿš€๐Ÿ˜Ž๐Ÿ˜Ž This is just fuยค&#ng great !!!! ๐Ÿ˜

Quick test on both 1. and 2. failed. As far as i can see in error log, it hasn't read source list properly or (can't find the separator ',')

I'll write more extensively, when you've had the chance to check code.

I've changed settings to one single speaker for test, as the Sonos group obviously doesn't have the states for 'source list' or anything else for that matter. So if Sonos groups should be controlled (most people probably use groups as they have more than one Sonos speaker), the 'master' speakers states has to be used. Master speaker is always the first speaker in 'sonos_group' list exposed in HA.

image

Below error log for Custom Media Player controller example (1). Similar errors thrown when using the standard E1810Mediaplayer (2)

  module: controllerx
  class: E1810MediaPlayerController
  controller: sensor.0x90fd9ffffe0cbd69_action
  integration: z2m
  volume_steps: 20
  media_player: media_player.stue
  #actions:
  #  - toggle
  #  - brightness_up_click
  #  - brightness_down_click
  #  - arrow_left_click
  #  - arrow_right_click
  #  - arrow_left_hold
  #  - arrow_right_hold
  #  - brightness_up_hold
  #  - brightness_up_release
  #  - brightness_down_hold
  #  - brightness_down_release

example_app:
  module: controllerx
  class: CustomMediaPlayerController
  controller: sensor.0x90fd9ffffe0cbd69_action
  integration: z2m
  media_player: media_player.stue
  mapping:
    arrow_left_click: previous_source
    arrow_right_click: next_source

2020-02-22 11:08:45.042278 WARNING example_app: ------------------------------------------------------------ 2020-02-22 11:08:45.043983 WARNING example_app: Unexpected error in worker for App example_app: 2020-02-22 11:08:45.045561 WARNING example_app: Worker Ags: {'id': 'cfad4c00164a4364875fe49ed1d559bb', 'name': 'example_app', 'objectid': 'b6a40a0796d74bebb260d473fd7c9c69', 'type': 'state', 'function': <bound method Z2MIntegration.callback of <core.integration.z2m.Z2MIntegration object at 0x74a30c40>>, 'attribute': 'state', 'entity': 'sensor.0x90fd9ffffe0cbd69_action', 'new_state': 'arrow_right_click', 'old_state': '', 'pin_app': True, 'pin_thread': 8, 'kwargs': {'__thread_id': 'MainThread'}} 2020-02-22 11:08:45.047313 WARNING example_app: ------------------------------------------------------------ 2020-02-22 11:08:45.049823 WARNING example_app: Traceback (most recent call last): File "/usr/lib/python3.8/site-packages/appdaemon/threading.py", line 709, in async_worker await funcref(entity, attr, old_state, new_state, self.AD.state.sanitize_state_kwargs(app, args["kwargs"])) File "/config/appdaemon/apps/controllerx/core/integration/z2m.py", line 15, in callback await self.controller.handle_action(new) File "/config/appdaemon/apps/controllerx/core/controller.py", line 109, in handle_action await action(args) File "/config/appdaemon/apps/controllerx/core/controller.py", line 28, in _action_impl await method(self, args, **kwargs) File "/config/appdaemon/apps/controllerx/core/type/media_player_controller.py", line 43, in change_source_list source_list = source_list.split(",") AttributeError: 'list' object has no attribute 'split' 2020-02-22 11:08:45.051271 WARNING example_app: ------------------------------------------------------------

xaviml commented 4 years ago

That could be because of the attribute is already a list, I thought it would be a string, this is why I splitted. I will try to fix that or you can go to that file as you have the path and comment that line with # and see if that fixes it.

htvekov commented 4 years ago

I'll check...

htvekov commented 4 years ago

Yep. Commenting line 43 in media_player_controller.py did it ๐Ÿ‘๐Ÿ˜Ž All working including 'circular' (from first to last and vice versa)

Custom Media Controller and E1810 Media Controller working as expected.

Only issue is playing with Sonos Groups. I believe that it could be solved like this:

  1. Add single media player in config file (Expected 'master' speaker)
  2. Read sonos_group entities and apply volume change to all entities in group
  3. Read source_list from first sonos_group entity

Hmm... Have to check what states are for a speaker pulled from a group and playing on its own ??

htvekov commented 4 years ago

OK. If Sonos group is 'dissolved' (By Sonos app or via HA) then speakers sonos_group attribute remains with their 'own' entity in group

image

htvekov commented 4 years ago

One last thing about sonos_group. Applying source changes to any single group entity (regardless of master or slave entities) reflects on all other entities in group.

htvekov commented 4 years ago

Hi' Xavi.

I forgot to test multiple service calls using same controller (trigger) and sharing same key mapping, but as different app instances and/or different classes.

Thought that this should work, but it don't ? If both example_app and office_speaker_source_list are active, then light won't flash but source changes. If example app is 'commented out' then light will flash.

Perhaps I misunderstood how multiple service calls (using different apps with identical keys) should be used ??

Regards Henning

office_controller:
  module: controllerx
  class: E1810MediaPlayerController
  controller: sensor.0x90fd9ffffe0cbd69_action
  integration: z2m
  volume_steps: 20
  media_player: group.sonos_all
  actions:
    - toggle
    - brightness_up_click
    - brightness_down_click
  #  - arrow_left_click
  #  - arrow_right_click
  #  - arrow_left_hold
  #  - arrow_right_hold
    - brightness_up_hold
    - brightness_up_release
    - brightness_down_hold
    - brightness_down_release

example_app:
  module: controllerx
  class: CustomMediaPlayerController
  controller: sensor.0x90fd9ffffe0cbd69_action
  integration: z2m
  media_player: media_player.stue
  mapping:
    arrow_left_click: previous_source
    arrow_right_click: next_source

office_speaker_source_list:
  module: controllerx
  class: CallServiceController
  controller: sensor.0x90fd9ffffe0cbd69_action
  integration: z2m
  mapping:
    arrow_left_click:
      - service: light.turn_on
        data:
          entity_id: light.kontor_loft
          flash: short
#     - service: media_player.select_source
#       data:
#         entity_id: group.sonos_all
#         source: DR P3
    arrow_right_click:
#     - service: media_player.select_source
#       data:
#         entity_id: group.sonos_all
#         source: DR P4 Fyn 96.8 (Nyheder)
      - service: light.turn_on
        data:
          entity_id: light.kontor_loft
          flash: short
xaviml commented 4 years ago

Hi @htvekov,

Regarding the Sonos group, how does it behave when the Sonos are grouped with the group integration. Could you please open a new issue for it and describe what does work and what does not? Let's see if we can find a generic solution for it, I cannot read the sonos_group since that would just solve the problem to Sonos devices. Thanks :)

xaviml commented 4 years ago

Hi again @htvekov,

I observed the same as you (calling different actions in different app instances) and apparently that was a very nasty bug. I could fix it and I test it with a real example:

livingroom_light:
  module: controllerx
  class: E1810Controller
  controller: sensor.livingroom_controller_action
  integration: z2m
  light: light.livingroom
  smooth_power_on: true
test:
  module: controllerx
  class: CallServiceController
  controller: sensor.livingroom_controller_action
  integration: z2m
  mapping:
    toggle:
      - service: notify.telegram_test
        data:
          message: Test message 1
      - service: notify.telegram_test
        data:
          message: Test message 2

With this configuration I can press the toogle button and it does the following:

These changes can be found in v2.3.0b6 as well as the fix for the "source_list" (Removing line 43 in media_player_controller.py).

Let me know if this fixed the problem related to calling different actions with different app instances. Sorry for all the bugs related to this, but I really appreciate it that you are finding them :)

htvekov commented 4 years ago

Hi' Xavi.

Multiple service calls, sharing same key, now works with latest beta v2.3.0b6 Problem solved ! ๐Ÿ‘๐Ÿ˜Š

Don't worry about the bugs. I've never seen code that was flawless in first beta or beta no. #15 for that matter ๐Ÿ˜‰ And as your code rapidly becomes more and more complex, bugs are bound to pop up. I would be more worried if there was no bugs... ๐Ÿ›๐ŸฆŸ๐Ÿ•ท๐Ÿž

htvekov commented 4 years ago

Regarding the Sonos 'issue' the main problem AFAIK is that Sonos implementation is unique, as they (still) can't be integrated with other speakers in a multiroom setting in eg. Google Home. So nothing generic about Sonos I'm afraid in general and in HA implementation. Handling the Sonos 'specials' should in my opinion have been handled properly on a lower level in HA (media.player integration). But I'm not fully aware of the problems concerning Sonos integration.

My Sonos group is manually added. There's no automatic Group creation in HA and now autogenerated groups in HA are depricated all together very soon.

So my Sonos group only contains the entities:

entity_id:
  - media_player.kokken
  - media_player.kitchen
  - media_player.stue
order: 4
friendly_name: sonos_all

I'll open a new issue regarding Sonos, Xavi. I'll try and describe what's working and what's not. Will also describe what I've observed about group behaviour and using single speakers. And describe how I with the numerous possibilities in ControllerX can tweak my way to the desired behaviour ๐Ÿ˜

xaviml commented 4 years ago

Okay, we will leave this issue closed then and discuss the Sonos group in the new issue. Thanks @htvekov