Spotify connect to sonos over different subnets and a firewall

We have some nice Sonos speaker in our network. Of course we devided our network in private and guest. Guest just have acces to the internet.

Sometimes we want our guests to be able to choose the music. In the past they used a tablet for this but they had no access to their own playlists.

Since some weeks Spotify connect is able to control Sonos speaker to play music on them. So why not allow devices from guest-LAN to acces Sonos speaker to control them via Spotify connect?

Our setup: We have a linux box with iptables as a gateway between these two subnets (seperated by VLANs).

Spotify connect uses mDNS for discovery so I sat up the avahi deamon and enabled two options in the configuration.

apt-get install avahi-daemon


Also you need a tiny hole in your firewall. The first rule is to allow the access to the mDNS daemon. The other rules are one per Sonos device you want to allow acces to. We have „private“ Sonos devices we don’t want to share so we have not allowed all of them.

/sbin/iptables -A INPUT -p udp --dport 5353 -j ACCEPT -m comment --comment "mDNS for Spotify"
/sbin/iptables -I FORWARD --destination 192.168.X.Y -p tcp --dport 1400 -j ACCEPT -m comment --comment "Spotify to Sonos Wohnzimmer"
/sbin/iptables -I FORWARD --destination 192.168.X.Z -p tcp --dport 1400 -j ACCEPT -m comment --comment "Spotify to Sonos Kueche"

Spotify uses the local network just for gaining control – all the control itself is made via internet.

Calling vRO (VMware Orchestrator) Workflows via REST API

Last year I integraded some workflow calls in internal websites. The vRO offers a REST API for this. Unfortunately it’s not documented very well so I had to spend some time to figure things out.

This monday in Barcelona (VMworld 2016) there was a Hackathon organized by Alan Renouf. I attended a team to build a HTML5 interface to show information grabbed by docker containers with pyvmomi from vCenter servers. The whole Hackathon was a pleasure!

There was also a team building a HTML5 page to call vRO Workflows through the REST API. They get really close, they get the UI running and grabbed all available workflows via API. But as I know, the hard part follows up 😉
I’d like to share my experience in this post.

Firs of all I began with this guide:
For the beginning it was pretty good but if you really want to use the API it gets not far enough.

To view the API in your Browser jus visit: https://<your vRO>:8281/vco/api/
To view the Docs visit: https://<your vRO>:8281/vco/api/docs/index.html

If you are using vRO 7 you are also able to run and test alle the available API calls with the last link. This is much better than the Docs of vRO 6.

In general you can talk to the vRO in two formats: JSON or XML
To change between these formats you have to set HTTP headers:

for JSON:

Accept: application/json
Content-Type: application/json

for XML:

Accept: application/xml
Content-Type: application/xml


How to run a workflow

To run a workflow you just need to do a POST to this URL: https://<your vRO>:8281/vco/api/workflows/<workflow ID>/executions
You can look up the workflow ID in your vRO Client.

If you get HTTP 202 your workflow started successfull – just start – it does not say anything about the result of your run.

Also you get back a HTTP Header with the parameter „Location“ – which looks like:
https://<your vRO>:8281/vco/api/workflows/<workflow ID>/executions/<execution ID>/

The execution ID is the ID of your running workflow – you need it to get the status of your workflow run, get output parameters, …


How to get a status of my workflow run

To get the status you use the URL you get back in the Location-Parameter of the HTTP Header:
https://<your vRO>:8281/vco/api/workflows/<workflow ID>/executions/<execution ID>/

Here you get all the information you need:

  • state (completed, failed, …)
  • Your provided input-parameter
  • output-parameter (only if state = „completed“)
  • start-date
  • end-date
  • started-by
  • content-exception (only if state = „failed“)


Passing parameters to your workflow

Until now this was all standard REST and nothing special – but now it gets interesting 😉
The easiest way to find out how to pass arguments is to run the workflow from vRO client and take a look at the execution with your browser and the REST API.

To pass parameters you have to provide some information in your body while you do your POST request:

 <execution-context xmlns="">
     <parameter type="VC:HostSystem" name="inHosts" scope="local">
       <sdk-object type="VC:HostSystem" id="your.vcenter.fqdn/host-1234"/>
     <parameter type="string" name="inShareString" scope="local">
       <string>this is a string</string>
     <parameter type="Text" name="inBemerkung" scope="local">
         <string>this is a text</string>
     <parameter type="number" name="inNumber" scope="local">
     <parameter type="boolean" name="inBoolean" scope="local">

In this example there are five input Parameters:

  1. Name: inHosts (Type „VC:HostSystem“)
  2. Name: inShareString (Type „string“)
  3. Name: inBemerkung (Type „Text“)
  4. Name: inNumber (Type „number“)
  5. Name: inBoolean (Type „boolean“)

In JSON it looks like:

  "parameters": [
      "type" : "VC:HostSystem",
      "name" : "inHosts",
      "scope" : "local",
      "value" : {
        "sdk-object" : {
          "type" : "VC:HostSystem",
          "id" : "your.vcenter.fqdn/host-1234"
      "type" : "string",
      "name" : "inShareString",
      "scope" : "local",
      "value" : {
        "string" : {
          "value" : "this is a string"
      "type" : "Text",
      "name" : "inBemerkung",
      "scope" : "local",
      "value" : {
        "string" : {
          "value" : "this is a text"
      "type" : "number",
      "name" : "inNumber",
      "scope" : "local",
      "value" : {
        "number" : {
          "value" : "42"
      "type" : "boolean",
      "name" : "inBoolean",
      "scope" : "local",
      "value" : {
        "boolean" : {
          "value" : "true"

This may contain errors – I’ve just disassembled my running code – forgot to document it last year 😀

As you see the input parameter „Text“ has also the type „string“.

Also you can see how to pass objects – any object is an „sdk-object“.
The value is the MoId (Manage Object ID). In case of the vCenter Plugin it is „vcenter-name/moid“.

Get the MoId you have to pass

There are several way to get it.

  1. For vCenter objects you can get the moid of your VMs, Hosts, .. from MOB (https://<your vcenter/mob)
  2. You can search the vRO inventory by using this URL:
    https://<your vRO>:8281/vco/api/catalog/VC/HostSystem/?conditions=name=<esxi name>
  3. I already have a database with object-name <=> moid relation for other functions of my webpage so I just use this (there is a discovery run every night and my workflows update the database over day per single operation)
  4. For other departments in our company I just build workflows with string input and do the object-search inside the workflow – then they don’t need to bother about object types and IDs and just have to pass me the name


Example code and functions

I’ve pushed some example code to github:

Feel free to do pull requests with your own examples.


Amazon Dash Button mit ISC DHCPD und FHEM

Einen WLAN-Knopf für nur 5€? Den muss man doch ausprobieren 😉


Das die Amazon Dash Buttons umfunktioniert werden können ist mittlerweile weitläufig bekannt. Die aktuellen Lösungen reagieren fast alle entweder auf den DHCP-Request vom Dash Button oder dem nachfolgendem ARP Request. Meistens wird ein Script verwendet, welches auf die Netzwerkpakete horcht und bei Eintreffen eine Aktion ausführt.

Im FHEM-Forum taucht auch schon das erste Modul auf. Dieses öffnet einen Listener auf Port 67 (DHCP) und lauscht direkt auf die ankommenden DHCP-Requests.
Das FHEM-Modul können wir leider nicht verwenden, da wir auf dem FHEM-Server auch gleichzeitig unseren DHCP Server laufen lasse und der Port somit schon belegt ist.Also würde mir nur die Script-Lösung bleiben. Aber wozu ein extra Script laufen lassen, wenn das auch mein DHCP-Server übernehmen könnte?!

Der von uns eingesetzte ISC DHCPD ist in der Lage nach einem DHCP-Request ein Script auszuführen.

host dashbutton-nerf {
        hardware ethernet 44:65:0d:xx:xx:xx;
        fixed-address 192.168.xx.xx;
        option routers; # dummy
        on commit { execute("/opt/fhem/","7072","set dummy_Shared_dashButton_Nerf on"); }

Das Script wird natürlich nur bei dem Button mit genau der MAC-Adresse ausgeführt. Zusätzlich überschreibe ich nur für den Button den Default-Gateway, damit der Button gar nicht erst zu Amazon durch kommt.

Achtung: Wenn AppArmor im Einsatz ist, muss man etwas entschärfen – der mag es nicht wenn der DHCP-Server plötzlich Scripte ausführt 😉

Die passende FHEM-Config besteht aus einem dummy und einem notify:

define dummy_Shared_dashButton_Nerf dummy;
attr dummy_Shared_dashButton_Nerf room Wohnzimmer;
attr dummy_Shared_dashButton_Nerf setList on off;
attr dummy_Shared_dashButton_Nerf event-on-change-reading .*;
dummy_Shared_dashButton_Nerf:on {
  fhem "define at_Shared_dashButton_Nerf_reset at +00:00:40 set dummy_Shared_dashButton_Nerf off";
  if(Value("dummy_Shared_Wohnzimmer_use_PlayStation4") eq "off"){
    fhem "set dummy_Shared_Wohnzimmer_use_PlayStation4 on";
    fhem "set dummy_Shared_Wohnzimmer_use_PlayStation4 off";

Der Notify reagiert nur auf ein „on“ vom Dummy und setzt den Status mit dem neu definierten at-Device nach 40 Sekunden wieder zurück auf „off“. Das nutze ich zum entprellen des Buttons. Mir ist beim Testen aufgefallen, das es sein kann, das der Button sich mehrmals meldet und der DHCP somit das Script mehrfach ausführt. Das Attribute „event-on-change-reading“ hilft beim entprellen, indem es das notify nur bei State-Changes ausführt und nicht jedes mal wenn der State gesetzt wird.
Es sind übrigens 40 Sekunden, weil der Dash-Button ganze 35 Sekunden wach ist, wenn man ihn nicht zu Amazon durchkommen lässt. Kommt er durch und bekommt eine Antwort von Amazon ist er nur 8 Sekunden wach. Wenn noch jemand einen Tipp hat wie man ihm den Call-Home abgewöhnt und trotzdem bei 8 Sekunden bleibt, gerne melden 😉 Man-in-the-middle wird wohl nichts, da die Kommunikation mit HTTPS gesichert ist und self-signed Zertifikate nicht angenommen werden.

Ich werde den Button jetzt unter den Wohnzimmertisch kleben und mich bei jedem Tastendruck freuen wenn die Beleuchtung in den Kino-Modus geht, die Leinwand runter fährt, der Beamer sich einschaltet, … 😀 Und beim nächsten Tastendruck wird der Kino-Modus wieder beendet.

MQTT Verstärkerumschalter mit dem ESP8266

Sonos ist ein geiles System, aber … Von Flexibilität haben die da leider noch nichts gehört. Auch die wollen Geld schöffeln und für jeden Zweck ein eigenes Gerät verkaufen. Unser Problem: Wir haben einen Sonos Connect Amp im Wohnzimmer und zusätzlich einen AVReceiver als Verstärker und HDMI-Umschalter für den Beamer. Jeder für sich braucht ein Lautsprecherpaar, d.h. es stehen vier Lautsprecher in der Gegend rum. Wir haben uns unterschiedliche Konfigurationen überlegt, auch mit anderen Sonos Geräten, kamen aber nie auf eine Zufriedenstellende und/oder bezahlbare Lösung. Also durfte ich endlich wieder was basteln 😀

Problem beim Umschalten zwischen zwei Verstärkern ist, dass die Verstärker niemals miteinander verbunden werden dürfen. Bei einem Lautsprecherumschalter ist dies nicht gegeben, da wird nicht darauf geachtet weil es unwichtig ist. Die am Markt verfügbaren Verstärkerumschalter sind alle technisch veraltet (Automation? Schnittstellen?) oder wieder sehr sehr teuer. Wir haben so viel Zeit in die Hausautomation gesteckt, da will ich doch nicht immer von Hand umschalten müssen. Vor allem nicht wenn ich bereits auf dem Sofa liege 😉

Also ging es an einen Plan für einen Umschalter über Relais mit dem ESP8266 (Microcontroller mit WLAN). Ich arbeite bewusst mit vier Relais, um ein zeit verzögertes aus- und einschalten beim Wechsel zwischen den beiden Verstärkern realisieren zu können. Zudem wurde eine Schutzschaltung in Hardware eingebaut die beim Schalten des einen Verstärkers die Schalteingänge für die Relais des zweiten Verstärkers auf Masse zieht, damit diese nicht versehentlich gleichzeitig geschaltet werden können. Sollte es nun passieren, dass alle Relais gleichzeitig geschaltet werden, gibt es keinen Kurzschluss, sondern ein Verstärker gewinnt immer.


Neben den im Schaltplan ersichtlichen Bauteilen kam noch folgendes dazu:

Da die Front- und Rückseite des Gehäuses aus Metall ist und die Anschlussklemmen nicht isoliert sind, habe ich die Rückseite durch eine Epoxy-Platte ersetzt.


Als Software kommt MicroPython für den ESP8266 zum Einsatz. Aktuell gibt es das Binary noch nicht öffentlich sondern nur eine Preview für Unterstützer über Kickstarter.

Fazit: Die Preview ist schon mega geil! Macht viel mehr Laune damit den ESP zu bestücken als mit der ArduinoIDE. Zudem hat man eine direkte Python-Konsole auf dem ESP und kann per Kommandozeile mit dem Microcontroller arbeiten. Das macht das Programmieren und Debuggen um einiges einfacher.

Hier ein erster Entwurf des Python Codes für den Umschalter – ist aber eher ein Proof of Concept – vernünftiger Code muss noch nachträglich gebaut werden 😀
Den jeweils aktuellsten Code gibts auf github:

PW='WLAN PW here'

import network
sta_if = network.WLAN(network.STA_IF)
if not sta_if.isconnected():
    print('connecting to network...')
    while not sta_if.isconnected():
print('network config:', sta_if.ifconfig())
ap_if = network.WLAN(network.AP_IF)

import machine
import time
from umqtt.simple import MQTTClient
import ubinascii
ledr = machine.Pin(4, machine.Pin.OUT)
ledl = machine.Pin(2, machine.Pin.OUT)
switch = machine.Pin(5, machine.Pin.IN)
amp1r = machine.Pin(14, machine.Pin.OUT)
amp1l = machine.Pin(13, machine.Pin.OUT)
amp2r = machine.Pin(16, machine.Pin.OUT)
amp2l = machine.Pin(12, machine.Pin.OUT)

act_amp = 1
new_amp = 1

def set_amp(n):
  global act_amp
  if n == 2:
    act_amp = 2
    mqtt.publish(MQTT_TOPIC_STATE, b"2")
    act_amp = 1
    mqtt.publish(MQTT_TOPIC_STATE, b"1")

def switch_pressed(p):
  global new_amp
  if act_amp == 1:
    new_amp = 2
    new_amp = 1

def mqtt_input(topic, msg):
  global new_amp
  if msg == b"2":
    new_amp = 2
    new_amp = 1

CLIENT_ID = ubinascii.hexlify(machine.unique_id())
print("MQTT connected to %s, subscribed to %s topic" % (MQTT_SERVER, MQTT_TOPIC))

switch.irq(trigger=machine.Pin.IRQ_FALLING, handler=switch_pressed)

while True:
  if new_amp != act_amp:

Der Code schickt übrigens auch jede Statusänderung über MQTT raus – egal ob manuell über den Taster, oder ob automatisiert über MQTT. So ist FHEM immer auf dem aktuellsten Stand.


In FHEM wird dann ein neues Device angelegt:

define MQTTD_Shared_AmpSwitch MQTT_DEVICE
attr MQTTD_Shared_AmpSwitch IODev MQTT_Shared
attr MQTTD_Shared_AmpSwitch alias Amp Switch
attr MQTTD_Shared_AmpSwitch devStateIcon 1:icoSONOSPLAYER_icon-ZP120 2:it_television@orange
attr MQTTD_Shared_AmpSwitch event-on-change-reading .*
attr MQTTD_Shared_AmpSwitch group 1.Entertainment
attr MQTTD_Shared_AmpSwitch publishSet amp ampswitch
attr MQTTD_Shared_AmpSwitch publishSet_amp ampswitch
attr MQTTD_Shared_AmpSwitch room Wohnzimmer
attr MQTTD_Shared_AmpSwitch stateFormat state
attr MQTTD_Shared_AmpSwitch subscribeReading_state ampswitch/state
attr MQTTD_Shared_AmpSwitch webCmd amp 1:amp 2


Zu den Schaltregeln – für den Anfang gibt es folgende:

  • Wenn ein Entertainment-Programm gestartet wird (Leinwand runterfahren, Beamer an, ..) wird der Umschalter auf den AVReceiver umgestellt
  • Beim Beenden des Entertainment-Programms wird der Umschalter wieder auf Sonos zurück gestellt
  • Wenn der Umschalter auf den AVReceiver umgeschaltet wird, wird die Wiedergabe des Sonos gestoppt