My boring Blog

Home page di Mauro Frigerio
it en

Comando per bambini per speaker Sonos

29-09-2021 Tempo di lettura 6 minuti article

TL;DR

Utilizzando un ESP32 e Home Assistant con ESPhome è possibile controllare senza applicazione degli speaker Sonos (o altri media player). Questo è il mio progetto per permettere a mia figlia di gestire la musica a casa nostra (quando vogliamo).

Intro

Stiamo sistemando una casa ed è arrivato il tempo di fare grandi pulizie tra tutte le cose che abbiamo accumulato. In un raid di pulizia è stato scoperto un vecchio registratore a cassette per bambini, purtroppo non è più funzionante (lo farò funzionare prima o poi). Questo ritrovamento ci ha reso attenti sul fatto che la nostra bambina non può facilmente ascoltare la musica visto che ora è tutta in streaming e servono delle app sul telefono per poterla ascoltare. Non come ai nostri tempi, quando eravamo giovani…

La bambina potrebbe gestire tranquillamente lo smartphone, ma è un po' troppo presto. Così ho sviluppato una soluzione alternativa, creando una scatola che permettesse di gestire semplicemente degli speaker Sonos.

Hardware

La casa è equipaggiata con varie speaker Sonos nelle stanze importanti, una di queste è quella della bambina. Nella sua stanza ho installato una scatola in legno con dei pulsanti e un lettore di carte RFID (rc522). Il tutto è gestito da un ESP32. In un primo tempo ho provato a collegare l’ESP a una batteria, ma ho rinunciato perché la durata di una carica era di poche settimane. Appena ho un po' di tempo però mi occuperò di sistemare anche questo aspetto.

Software

Home Assistant è una piattaforma fantastica e permette di interconnettere vari sistemi tra di loro. Spesso un produttore sviluppa un proprio sistema pensando solo ai propri prodotti. Raramente la piattaforma è aperta e permette un utilizzo da un sistema esterno. Ad esempio le lampade IKEA sono controllabili solo da interruttori IKEA. Mentre gli speaker Sonos sono gestibili sono con l’applicazione dello stesso produttore. Home Assistant distrugge queste bariere e permette (quasi sempre) di collegare tutto con tutto.

ESPhome

L’ESP32 nella scatola di comando è programmato tramite ESPhome. Nella scatola ho aggiunto questi switch:

  • play / pausa
  • prossima canzone
  • aumenter volume
  • ridurre volume
  • carta RFID presente

I primi quattro comandi sono chiari, mentre l’ultimo deve essere un po' spiegato. Ho aggiunto uno switch per sapere quando è inserita una carta RFID per poter risparmiare il cosumo d’energia e attivare il lettore solo quando necessario.

Questa è la configurazione di ESPhome:

substitutions:
  display_name: Ci Player
esphome:
  name: ci-player-power
  platform: ESP32
  board: firebeetle32

logger:
api:
  password: !secret esphome_secret
ota:
  password: !secret esphome_secret

wifi:
  ssid: !secret wifi_ssid
  password: !secret wifi_password

spi:
  clk_pin: GPIO18
  mosi_pin: GPIO23
  miso_pin: GPIO19

rc522_spi:
  cs_pin: GPIO21
  reset_pin: GPIO22

binary_sensor:
  - platform: gpio
    pin:
      number: 25
    id: ci_player_power_play
    name: ${display_name} Play
    filters:
      - delayed_off: 10ms

  - platform: gpio
    pin:
      number: 26
    id: ci_player_power_next
    name: ${display_name} Next
    filters:
      - delayed_off: 10ms

  - platform: gpio
    pin:
      number: 14
    id: ci_player_power_volume_up
    name: ${display_name} Volume up
    filters:
      - delayed_off: 10ms

  - platform: gpio
    pin:
      number: 27
    id: ci_player_power_volume_down
    name: ${display_name} Volume down
    filters:
      - delayed_off: 10ms

  - platform: gpio
    pin:
      number: 4
    id: ci_player_power_card_presence
    name: ${display_name} Card presence
    filters:
      - delayed_off: 10ms

  - platform: rc522
    uid: AB-35-B5-22
    name: ${display_name} Playlist 1
  - platform: rc522
    uid: 1B-8E-B6-22
    name: ${display_name} Playlist 2
  - platform: rc522
    uid: 5B-AE-A2-22
    name: ${display_name} Playlist 3
  - platform: rc522
    uid: 20-25-5C-2F
    name: ${display_name} Playlist 4

Automazioni in Home Assistant

Il collegamento tra il box di comando e gli speaker Sonos è fatto da due automazioni di Home Assistant. L’esperienza e la gestione sono rese migliori grazie alle seguenti variabili:

  • input_boolean.ci_player_blocco: è un interruttore per bloccare la possibilità di comandare gli speaker. Utile quando la bambina non fa la brava o deve dormire.
  • input_number.ci_player_volume_min: il valore minimo del volume. Attualmente è possibile solo il volume minimo o massimo.
  • input_number.ci_player_volume_max: il valore massimo del volume
  • input_number.ci_player_delay: ritardo d’esecuzione tra un comando e l’altro. Così la bimba riesce ad ascoltare qualcosa
  • input_select.ci_player_playlist: ultima playlist ascoltata
  • input_select.ci_player_speaker: scegliere lo speaker da comandare con la scatola di comando

La prima automazione si occupa di gestire i comandi (gli interruttori) e la seconda modifica la playlist da ascoltare in base alla scheda RFID inserita.

Il contenuto delle playlist è gestito tramite l’app di Sonos e devono essere visibili come sorgente audio quando si aggiunge la card media player in Lovelance della cassa Sonos.

Gestione comandi

alias: Ci-player POWER - comandi
description: PLAY - NEXT - VOLUME
trigger:
  - platform: state
    entity_id: binary_sensor.ci_player_power_play
    from: 'off'
    to: 'on'
    id: play
  - platform: state
    entity_id: binary_sensor.ci_player_power_next
    from: 'off'
    to: 'on'
    id: next
  - platform: state
    entity_id: binary_sensor.ci_player_power_volume_down
    from: 'off'
    to: 'on'
    id: volume_down
  - platform: state
    entity_id: binary_sensor.ci_player_power_volume_up
    from: 'off'
    to: 'on'
    id: volume_up
condition:
  - condition: state
    entity_id: input_boolean.ci_player_blocco
    state: 'off'
action:
  - choose:
      - conditions:
          - condition: state
            state: 'on'
            entity_id: binary_sensor.ci_player_power_play
        sequence:
          - choose:
              - conditions:
                  - condition: template
                    value_template: >-
                      {{ is_state(states('input_select.ci_player_speaker'),
                      'playing') }}                      
                sequence:
                  - service: media_player.media_play_pause
                    data_template:
                      entity_id: '{{ states (''input_select.ci_player_speaker'') }}'
            default:
              - service: media_player.select_source
                data_template:
                  source: '{{ states(''input_select.ci_player_playlist'') }}'
                  entity_id: '{{ states(''input_select.ci_player_speaker'' ) }}'
      - conditions:
          - condition: state
            entity_id: binary_sensor.ci_player_power_next
            state: 'on'
        sequence:
          - service: media_player.media_next_track
            data_template:
              entity_id: '{{ states(''input_select.ci_player_speaker'' ) }}'
      - conditions:
          - condition: state
            entity_id: binary_sensor.ci_player_power_volume_down
            state: 'on'
        sequence:
          - service: media_player.volume_set
            data_template:
              volume_level: '{{ states(''input_number.ci_player_volume_min'') }}'
              entity_id: '{{ states(''input_select.ci_player_speaker'' ) }}'
      - conditions:
          - condition: state
            entity_id: binary_sensor.ci_player_power_volume_up
            state: 'on'
        sequence:
          - service: media_player.volume_set
            data_template:
              volume_level: '{{ states(''input_number.ci_player_volume_max'') }}'
              entity_id: '{{ states(''input_select.ci_player_speaker'' ) }}'
    default: []
  - delay: >-
      00:00:{% if states('input_number.ci_player_delay') | int <
      10-%}0{{states('input_number.ci_player_delay') | int}} {% else
      %}{{states('input_number.ci_player_delay') | int}} {% endif %}      
mode: single

Selezione playlist

alias: Ci-player POWER - playlist
description: Selezione delle playlist
trigger:
  - platform: state
    entity_id: binary_sensor.ci_player_power_card_presence
    from: 'off'
    to: 'on'
condition:
  - condition: state
    entity_id: input_boolean.ci_player_blocco
    state: 'off'
action:
  - delay:
      hours: 0
      minutes: 0
      seconds: 1
      milliseconds: 0
  - choose:
      - conditions:
          - condition: state
            entity_id: binary_sensor.ci_player_power_playlist_1
            state: 'on'
        sequence:
          - service: media_player.select_source
            data_template:
              source: Z_Playlist 1
              entity_id: '{{ states(''input_select.ci_player_speaker'' ) }}'
          - service: input_select.select_option
            data:
              option: Z_Playlist 1
              entity_id: input_select.ci_player_playlist
      - conditions:
          - condition: state
            entity_id: binary_sensor.ci_player_power_playlist_2
            state: 'on'
        sequence:
          - service: media_player.select_source
            data_template:
              source: Z_Playlist 2
              entity_id: '{{ states(''input_select.ci_player_speaker'' ) }}'
          - service: input_select.select_option
            data:
              option: Z_Playlist 2
              entity_id: input_select.ci_player_playlist
      - conditions:
          - condition: state
            entity_id: binary_sensor.ci_player_power_playlist_3
            state: 'on'
        sequence:
          - service: media_player.select_source
            data_template:
              source: Z_Playlist 3
              entity_id: '{{ states(''input_select.ci_player_speaker'' ) }}'
          - service: input_select.select_option
            data:
              option: Z_Playlist 3
              entity_id: input_select.ci_player_playlist
      - conditions:
          - condition: state
            entity_id: binary_sensor.ci_player_power_playlist_4
            state: 'on'
        sequence:
          - service: media_player.select_source
            data_template:
              source: Z_Playlist 4
              entity_id: '{{ states(''input_select.ci_player_speaker'' ) }}'
          - service: input_select.select_option
            data:
              option: Z_Playlist 4
              entity_id: input_select.ci_player_playlist
    default: []
mode: single

Conclusione

Grazie al box la bambina riesce a fare il piccolo dj, facendo partire e fermando la musica quando vuole. Oppure cambiare la playlist scegliendo la musica che preferisce.