- 
                Notifications
    
You must be signed in to change notification settings  - Fork 59
 
Manage Google Nest speaker groups
        Nate edited this page Nov 18, 2020 
        ·
        9 revisions
      
    This is an app that was created for managing Groups of Google Nest speakers.
Some of its features:
- When playing music on a group, change the volume to 
musicVolumeunless the volume has been overridden--in which case, use that. - Switch between a "speaking" volume when not playing music and restore the volume to your prior music volume.
 - Adjust the volume based on a day mode and night mode (e.g. once it's sunset, make Assistant talk quieter than during the day).
 
Hope this helps someone get more familiar with this awesome tool!
pyscript:
  apps:
  speaker_group_manager:
    - group: media_player.downstairs_group
      speakers: # the speakers in the group
        media_player.living_room_speaker:
          volumeOverride: None # this is here so we can write a little less code in the script ;)
          musicVolume: 0.25 # default volume for media
          speakingVolume: 0.50 # default volume for the Assistant
        media_player.kitchen_speaker:
          volumeOverride: None
          musicVolume: 0.30
          speakingVolume: 0.50registered_triggers = []
def loadApp(app_name, factory):
    if 'apps' not in pyscript.config:
        return
    
    if app_name not in pyscript.config['apps']:
        return
    for app in pyscript.config['apps'][app_name]:
        factory(app)
@time_trigger('startup')
def speakerGroupStartup():
    loadApp('speaker_group_manager', buildSpeakerGroup)
def buildSpeakerGroup(config):
    global registered_triggers
    
    ### Day/Night mode variables ###
    startNightMode = '22:30:00'
    dayMode = f'range(sunrise, {startNightMode})'
    nightMode = f'range({startNightMode}, sunrise)'
    ### config variables ###
    group = config.get('group')
    speakers = config.get('speakers')
    ### conditionals used in the @state_triggers ##
    useMusicVol = f'{group} == "playing"'
    useSpeakingVol = f'{group} != "playing"'
    ### Day mode versions ###
    @time_active(dayMode)
    @state_trigger(useMusicVol)
    def restoreGroupMusicVolume():
        setMusicVolume()
    @time_active(dayMode)
    @state_trigger(useSpeakingVol)
    def restoreGroupSpeakingVolume():
        handleSpeakingVolume()
    ### Night mode versions ###
    @time_active(nightMode)
    @state_trigger(useMusicVol)
    def restoreGroupNightMusicVolume():
        setMusicVolume(nightMode=True)
    @time_active(nightMode)
    @state_trigger(useSpeakingVol)
    def restoreGroupNightSpeakingVolume():
        handleSpeakingVolume(nightMode=True)
    ### Time triggers ###
    # Reset group overrides to defaults at sunrise
    @time_trigger('once(sunrise)')
    def resetGroupOverrides():
        global speakers
        speakers = config.get('speakers')
    # Change group speaker volumes to night mode
    @state_active(f'{group} != "playing"')
    @time_trigger(f'once({startNightMode})')
    def setGroupNightSpeakingVolume():
        setSpeakingVolume(nightMode=True)
    ### Pièce de résistance ###
    # Sets volume overrides for group speakers
    @task_unique(f'monitor_{group}_volume')
    @state_active(f'{group} == "playing"')
    @state_trigger(f'{group}.volume_level')
    def monitorGroupVolume():
        for speaker, volumes in speakers.items():
            volume = state.get(f'{speaker}.volume_level')
            volumes['volumeOverride'] = round(volume, 2)
    ### Helper functions ###
    def setMusicVolume(nightMode=False):
        for speaker, volumes in speakers.items():
            volOverride = volumes['volumeOverride']
            musicVol = volumes['musicVolume'] if (
                volOverride == 'None' or volOverride is None) else volOverride
            volume = musicVol if not nightMode else musicVol - 0.07
            media_player.volume_set(entity_id=speaker, volume_level=volume)
    def setSpeakingVolume(nightMode=False):
        for speaker, volumes in speakers.items():
            speakingVol = volumes['speakingVolume']
            volume = speakingVol if not nightMode else speakingVol - 0.15 # -15 speaking volume when in night mode: arbitrary
            media_player.volume_set(entity_id=speaker, volume_level=volume)
    def handleSpeakingVolume(nightMode=False):
        spotifyDelay = 0.85
        # sometimes the speakers become 'unavailable' while playing
        if state.get(group) != 'unavailable':
            # Spotify changes to 'paused' between songs; wait to alter volume
            if (state.getattr(group)).get('app_name') == 'Spotify':
                waitUntilPlaying = task.wait_until(
                    state_trigger=(f'{group} == "playing"'),
                    timeout=spotifyDelay
                )
                if waitUntilPlaying['trigger_type'] == 'timeout':
                    setSpeakingVolume(nightMode)
            else:
                setSpeakingVolume(nightMode)
    # register to global scope
    registered_triggers.append([
        monitorGroupVolume,
        restoreGroupMusicVolume,
        restoreGroupSpeakingVolume,
        restoreGroupNightMusicVolume,
        restoreGroupNightSpeakingVolume,
        resetGroupOverrides,
        setGroupNightSpeakingVolume
    ])