『瀚思彼岸』» 智能家居技术论坛

 找回密码
 立即注册
查看: 34552|回复: 24

[基础教程] 用Sonos音箱让音乐走哪跟哪 - 音乐跟随

[复制链接]

29

主题

921

帖子

4158

积分

论坛元老

Rank: 8Rank: 8

积分
4158
金钱
3227
HASS币
70
发表于 2021-4-15 02:25:29 | 显示全部楼层 |阅读模式
本帖最后由 relliky 于 2021-6-9 05:20 编辑

好久以前看过王自如的智能家视频,觉得音乐跟随很装逼很不错,上网查询一下资料后发现自己之前买的用于家庭影院的sonos居然也可以搞。就开始了动手之旅。

原本是客厅里有一个二手的回音壁playbar,打折买的两个play1作为环绕立体声。 后又收了一个二手的play3,然后把这两个play1换了位置。重新弄过以后,现在的房间配置是
客厅: sonos playbar 回音壁
2楼主卧: sonos play 1 音箱
1楼走廊: sonos play 1 音箱
厨房: sonos play 3 音箱

加上我家里到处都有小米的人体传感器,基本上就可以开始动手了。

想实现的功能有:
1. 在用sonos开始放歌的时候,人走到哪,那里的附近的音箱就开始和主音箱组合,一起同步播放音乐。
2. 人离开那个区域一段时间后,那里的音箱就断开同步,且暂停播放。
3. 如果所有的房间都没有人,一段时间后,所有音乐暂停。
4. 不允许正在播放电视的客厅sonos playbar回音壁加入一起播放音乐。 比如有人在卧室里听音乐,客厅如果在看电视,他来到客厅以后不会让客厅打断电视而播放音乐。同理,客厅在看电视,人去了厨房,厨房音箱不会同步客厅的电视声音。如果客厅没有看电视,则把客厅音箱当作其他的普通sonos音箱进行音乐跟随。
5. 有个开关可以开关音乐跟随功能。设置个开关即可,我这里叫follow_music。如果音乐跟随被关闭了,只是普通的播放音乐。当人离开该房间后,仍然关闭该房间音乐。

首先要设置一个主音箱 master speaker, 同时我们需要维护主音箱让HA在任何时候知道主音箱是哪个。同时添加一个开关用于控制是否启动音乐跟随
input_select:
  music_controller:
    name: Sonos Music Master Speaker
    options:
      - master_room_sonos
      - kitchen_sonos
      - living_room_sonos
      - first_corridor_sonos


然后维护好主音箱,如果只有一个音箱在播放,把它设置成主音箱。
- alias: S- Set Music Master Speaker and Mantain Follow Music When The Only One Is Playing
  trigger:
  - platform: state
    entity_id:
      - media_player.living_room_sonos
      - media_player.master_room_sonos
      - media_player.kitchen_sonos
      - media_player.first_corridor_sonos
    to:
      - "idle"
      - "playing"
      - "paused"
      - "off"
  - minutes: /5
    platform: time_pattern      
  condition: []
  action:
  - choose:
    # IF - only kitchen sonos is playing - set it as master speaker
    - conditions:
      - condition: state
        state:     "playing"
        entity_id: media_player.kitchen_sonos
      - condition: not
        conditions:
        - condition: state
          state:     "playing"
          entity_id: media_player.living_room_sonos
        - condition: state
          state:     "playing"
          entity_id: media_player.master_room_sonos
        - condition: state
          state:     "playing"
          entity_id: media_player.first_corridor_sonos         
      sequence:
        - service: input_select.select_option
          entity_id: input_select.music_controller
          data:
            option:  kitchen_sonos
    # ELIF other rooms needs to be set up as well
    ##################################################
    # SKIPPING SIMILAR CODE
    ##################################################



如果有人进入某房间,则将那个房间的sonos同步到主音箱,主音箱保持不变,新加的同步音箱成为副音箱,且同步主音箱音量到副音箱。同步音量的时候,如果不同设备同音量下响度不一样,可以进行一些微调让它们保持一个合适的响度。我的play3 音量要比playbar和play1 需要多加10%的音量达到比较平衡的声音
add_sonos_into_speaker_group:
  mode: queued
  alias: Add Sonos Speaker Into the Speaker Group
  fields:
    target_player:
      description: "Sonos player name that need to be added into the group"
      example: "media_player.master_room_sonos"
  sequence:
    - condition: template
      value_template: >
        {% if target_player is not none and target_player != false and target_player != '' %}
          true
        {% else %}
          false
        {% endif %}
    # The target player must not be playing anything
    - condition: template
      value_template: >
        {% if states(target_player) != 'playing' %}
          true
        {% else %}
          false
        {% endif %}
    # First set the target player to the same volume as the controller
    # Play:3 sounds level needs to be offset for setting up Play:1/Playbars
    - service: media_player.volume_set
      data_template:
        entity_id: >
          {% if target_player is not none %}
            {{ target_player }}
          {% endif %}
        volume_level: >
          {% for state in states.media_player if state.entity_id == 'media_player.' + states('input_select.music_controller') %}
            {% if   states('input_select.music_controller') != 'kitchen_sonos' and target_player == 'media_player.kitchen_sonos' %}
              {{ state.attributes.volume_level + 0.1 }}
            {% elif states('input_select.music_controller') == 'kitchen_sonos' and target_player != 'media_player.kitchen_sonos' %}
              {{ state.attributes.volume_level - 0.1 }}
            {% else %}  
              {{ state.attributes.volume_level }}
            {% endif %}
          {% endfor %}
    # Now join the player into the group twice in case sometimes it didn't manage to join in for certain cases
    - service: sonos.join
      data_template:
        master: media_player.{{ states('input_select.music_controller') }}
        entity_id: >
          {% if target_player is not none %}
            {{ target_player }}
          {% else %}
            media_player.living_room_sonos
          {% endif %}

如果有人离开某房间一段时间,则将那个房间的sonos从主音箱断开,根据移除的sonos,重新设置主音箱
remove_sonos_from_speaker_group:
  alias: Remove Sonos Speaker From the Speaker Group and Update the Master Speaker
  mode: queued
  fields:
    target_player:
      description: "Sonos player that need to be removed from the group"
      example: "media_player.master_room_sonos"
  sequence:
    - condition: template
      value_template: >
        {% if target_player is not none and target_player != false and target_player != '' %}
          true
        {% else %}
          false
        {% endif %}
    # The target player must be playing
    - condition: template
      value_template: >
        {% if states(target_player) == 'playing' %}
          true
        {% else %}
          false
        {% endif %}
    # The target is not the soundbar that is playing TV sound
    - condition: template
      value_template: >
        {% if target_player is not none and state_attr(target_player, 'media_title') != 'TV' %}
          true
        {% else %}
          false
        {% endif %}
    # Update the master speaker in the group
    - service: input_select.select_option
      entity_id: input_select.music_controller
      data:
        option: >
          {% set ns = namespace() %}
          {% set ns.primary_speaker   = 'none' %}
          {% set ns.secondary_speaker = 'none' %}
          {# set the pri_speaker and sec_speaker #}
          {% for speaker in state_attr(target_player, "sonos_group") %}
            {% if loop.index == 1 %} 
              {% set ns.primary_speaker   = speaker|regex_replace(find='media_player.', replace='', ignorecase=False) %}
            {% elif loop.index == 2 %} 
              {% set ns.secondary_speaker = speaker|regex_replace(find='media_player.', replace='', ignorecase=False) %}
            {% endif %}
          {% endfor %}
          {# use the second speaker as master speaker if target speaker is currently the master #}
          {% if target_player == ('media_player.' + ns.primary_speaker) and ns.secondary_speaker != 'none' %}
            {{ ns.secondary_speaker }}
          {% else %}
            {{ ns.primary_speaker }}
          {% endif %}
    # The target must be the slave to be removed from the group
    - condition: template
      value_template: >
        {% if target_player != 'media_player.' + states('input_select.music_controller') %}
          true
        {% else %}
          false
        {% endif %}
    - service: sonos.unjoin
      data:
        entity_id: >
          {% if target_player is not none and target_player != false and target_player != '' and target_player != 'None'%}
            {{ target_player }}
          {% else %}
            media_player.living_room_sonos
          {% endif %}


如果只有该音箱在没有和其他音箱组合且人离开了一段时间,暂停播放这个房间音乐。这个是用来区别unjoin和pause。虽然强行unjoin一个没有和其他音箱组合的音箱也可以让它暂停播放,但有时会出现其他的问题,所以还是老老实实的暂停播放的好。
pause_sonos_if_sole_speaker_group:
  alias: Pause the Sonos Speaker if it is a Sole Speaker Group
  mode: queued
  fields:
    target_player:
      description: "Sonos player that need to be paused"
      example: "media_player.master_room_sonos"
  sequence:
    - condition: template
      value_template: >
        {% if target_player is not none and target_player != false and target_player != '' %}
          true
        {% else %}
          false
        {% endif %}
    # check it is a sole speaker
    - condition: template
      value_template: >
        {% for speaker in state_attr(target_player, "sonos_group") %}
          {% if loop.index == 1 %}  
            {% if loop.length == 1 %} 
              true  
            {% else %}      
              false      
            {% endif %}
          {% endif %}
        {% endfor %}
    - service: media_player.media_pause
      data:
        entity_id: >
          {% if target_player is not none and target_player != false and target_player != '' and target_player != 'None'%}
            {{ target_player }}
          {% else %}
            media_player.living_room_sonos
          {% endif %}


利用这些script,给每个房间根据人体传感器和音乐跟随开关写上人来和人走的自动化
#################################################################
#
# Sonos Speakers Grouping/Ungrouping Based On Motion Sensor
#
#################################################################
- alias: S-LR Living Room Group Its Speaker If People Present
  trigger:
    - platform: state
      from: "off"
      to: "on"
      entity_id:
        - binary_sensor.living_room_sofa_motion_sensor_motion
        - binary_sensor.living_room_tv_motion_sensor_motion
  condition:
    - condition: state
      entity_id: input_boolean.follow_music
      state: "on"
    # The controller player must be playing music (not paused)
    - condition: template
      value_template: >
        {% if states('media_player.' + states('input_select.music_controller')) == 'playing' %}
          true
        {% else %}
          false
        {% endif %}
  action:
    # Add this room speaker into the group
    - service: script.add_sonos_into_speaker_group
      data:
        target_player: media_player.living_room_sonos

- alias: S-LR Living Room Ungroup/Pause Its Speaker If No People
  trigger:
    - platform: state
      from: "on"
      to: "off"
      for: 00:10:00
      entity_id:
        - binary_sensor.living_room_sofa_motion_sensor_motion
        - binary_sensor.living_room_tv_motion_sensor_motion
    - minutes: /5
      platform: time_pattern
  condition:
    - condition: state
      entity_id:
        - binary_sensor.living_room_sofa_motion_sensor_motion
        - binary_sensor.living_room_tv_motion_sensor_motion
      for: 00:10:00
      state: "off"
  action:
    - choose:
        # IF - music follower is on
        - conditions:
            - condition: state
              entity_id: input_boolean.follow_music
              state: "on"
          sequence:
            # Remove this room speaker from the group
            - service: script.remove_sonos_from_speaker_group
              data:
                target_player: media_player.living_room_sonos
    # Pause this room speaker in case it is the last item in the group
    # or music follower is off
    # Remove this room speaker from the group
    - service: script.pause_sonos_if_sole_speaker_group
      data:
        target_player: media_player.living_room_sonos


这样就大功告成了。 搞了我几个晚上加周末,最终是调试完成了。 现在用sonos app打开音乐就可以走哪音乐跟到哪了。加上HA自带的airsonos的插件,就算不用sonos app听音乐,用手机airplay到sonos也是一样的。希望对买sonos的朋友有帮助。
还在学习怎么写template所以可能有些地方写的比较冗长一点,以后有时间可以把这些代码精简一下。




评分

参与人数 3金钱 +27 HASS币 +20 收起 理由
wlcdbb + 2
ttbye + 5 论坛有你更精彩!
+ 20 + 20 厉害了word楼主!

查看全部评分

回复

使用道具 举报

3

主题

150

帖子

941

积分

高级会员

Rank: 4

积分
941
金钱
791
HASS币
0
发表于 2021-4-15 09:57:13 | 显示全部楼层
这个适合家里人少的吧,人一多不是个个都想听音乐就会被家庭成员投诉了
回复

使用道具 举报

1

主题

266

帖子

2548

积分

金牌会员

Rank: 6Rank: 6

积分
2548
金钱
2282
HASS币
0
发表于 2021-4-15 10:11:20 | 显示全部楼层
厉害,谢谢分享
回复

使用道具 举报

0

主题

166

帖子

4183

积分

论坛元老

Rank: 8Rank: 8

积分
4183
金钱
4017
HASS币
0
发表于 2021-4-15 12:31:39 | 显示全部楼层
太棒了,謝謝大大分享
回复

使用道具 举报

39

主题

750

帖子

4707

积分

论坛元老

Freddy.

Rank: 8Rank: 8

积分
4707
金钱
3932
HASS币
0

最佳新人卓越贡献

发表于 2021-4-15 16:09:00 | 显示全部楼层
这样太秀了吧。。蒂花之秀
回复

使用道具 举报

74

主题

1942

帖子

7885

积分

元老级技术达人

积分
7885
金钱
5893
HASS币
430

活跃会员教程狂人

发表于 2021-4-15 16:53:03 | 显示全部楼层
本帖最后由 咸味土豆 于 2021-4-15 17:09 编辑

优秀,主要是得有sonos设备才行吖。我看到HA文档的media_player服务中,有media_player.join和media_player.unjoin 两个服务,不知道是不是可以用google home mini来搞事情,没看到可以玩音乐跟随的,google的群组功能和这个不一样,当然本身也是为了听个响。
所有过往,皆为序章。
回复

使用道具 举报

29

主题

921

帖子

4158

积分

论坛元老

Rank: 8Rank: 8

积分
4158
金钱
3227
HASS币
70
 楼主| 发表于 2021-4-15 17:01:08 | 显示全部楼层
Sourdough 发表于 2021-4-15 09:57
这个适合家里人少的吧,人一多不是个个都想听音乐就会被家庭成员投诉了 ...

对的,不过我的自动化里有一个叫follow_music的控制音乐跟随的开关。如果有人不想音乐跟随,可以随时关掉。
回复

使用道具 举报

29

主题

921

帖子

4158

积分

论坛元老

Rank: 8Rank: 8

积分
4158
金钱
3227
HASS币
70
 楼主| 发表于 2021-4-15 17:57:57 | 显示全部楼层
咸味土豆 发表于 2021-4-15 16:53
优秀,主要是得有sonos设备才行吖。我看到HA文档的media_player服务中,有media_player.join和media_player ...

google home mini 和alexa echo 都有类似的搞法,看phil大神博客最后一段写的是可以把家里的这些音箱都组合到一起,通过控制每个音箱的音量大小来控制音乐的关停,造成好像是某些音箱停了一样的效果,但是其实只是它的音量被静音了。

原文:https://philhawthorne.com/making ... ssistant-and-sonos/
最后一段讲“I don’t have Sonos, can I use this with something else?”
回复

使用道具 举报

32

主题

1993

帖子

5067

积分

论坛元老

Rank: 8Rank: 8

积分
5067
金钱
3074
HASS币
50
QQ
发表于 2021-4-15 22:58:45 | 显示全部楼层
膜拜大佬
我不生产技术,我只是技术的搬运工。
回复

使用道具 举报

74

主题

1942

帖子

7885

积分

元老级技术达人

积分
7885
金钱
5893
HASS币
430

活跃会员教程狂人

发表于 2021-4-18 16:58:12 | 显示全部楼层
relliky 发表于 2021-4-15 17:57
google home mini 和alexa echo 都有类似的搞法,看phil大神博客最后一段写的是可以把家里的这些音箱都组 ...

明白了,这思路可以,谢谢大佬提醒,有点开窍。
所有过往,皆为序章。
回复

使用道具 举报

您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

Archiver|手机版|小黑屋|Hassbian

GMT+8, 2024-4-27 01:20 , Processed in 0.333921 second(s), 34 queries .

Powered by Discuz! X3.4

Copyright © 2001-2021, Tencent Cloud.

快速回复 返回顶部 返回列表