Du bist hier:Start»Wissen»Smarthome»Heizung

Heizung mit dem Raspberry Pi 2 steuern

11.01.2016

Heizkörperthermostat

Der "Zoo" an Funkprotokollen fürs Smarthome ist ein Hinderungsgrund, warum das Smarthome noch keine breite Akzeptanz findet. Mit Bluetooth Smart ist nun erstmals ein standardisiertes Funkprotokoll auf dem Markt, das sich übers Smartphone ohne zusätzliche Geräte (Bridge) benutzen läßt. Bluetooth Smart wird auch als Bluetooth Low Energy (BLE) bezeichnet und als Bluetooth 4.0 spezifiziert. Mittlerweile sind zum Beispiel Steckdosen oder Heizkörperthermostate mit Bluetooth Smart auf dem Markt. Durch einen USB-Stick für Bluetooth 4.0 läßt sich ein Raspberry Pi 2 zur Smarthome-Zentrale erweitern. Der Raspberry Pi kann dann die einzelnen Heizkörperthermostate über Bluetooth Smart steuern. Der folgende Artikel wirft einen Blick auf das Heizkörperthermostat Comet Blue.

Hardware

Das Heizkörperthermostat wird unter verschiedenen Marken verkauft. Die Hardware der verschiedenen Modelle verhält sich intern gleich. Sie stammt von der Firma: EUROtronic. Xavax wird am teuersten verkauft. Das identische Comet Blue ist am günstigsten zu haben.

Name Preis Vertrieb über
Xavax Hama 40 Euro Media Markt
Sygonix HT100 BT 20 Euro Conrad
Comet Blue 20 Euro Real / Bauhaus

Lautstärke

Das folgende Video gibt einen Eindruck wie laut das Heizkörperthermostat beim Öffnen und Schließen ist.

Die Lautstärke hängt natürlich auch vom Ventil der Heizung ab. Aus meiner Sicht ist das Heizkörperthermostat fürs Schlafzimmer ungeeignet und bedarf einer langsameren und leiseren Motorsteuerung. Für gewöhnlich dauert das Öffnen und Schließen des Ventils nicht so lang wie im Video. Es gibt allerdings auch Fehlfunktionen der Firmware, bei denen das Thermostat mehrere Mal hintereinander komplett öffnet und schließt (bis zu 10 Mal bei meinem Thermostat).

Smartphone-Steuerung

Die Smartphone-Apps zur Steuerung des Thermostats sind Protokoll-kompatibel und sehr ähnlich. Die beste der drei Apps ist aus meiner Sicht "com.eurotronic_technology_gmbh.europrog.app".

Name App
Xavax Hama com.eurotronic_technology_gmbh.xavax.app
Sygonix HT100 BT com.eurotronic_technology_gmbh.sygonixht100bt.app
Comet Blue com.eurotronic_technology_gmbh.europrog.app

Auf Youtube findet sich eine Anleitung von siio.de zum Anschließen des Bluetooth Heizkörperthermostats und zur Benutzung der App.

Steuerung mit dem Raspberry Pi

Nun zum interessanten Teil. Mit einem USB-Stick für Bluetooth 4.0 (z.B. LogiLink Bluetooth Adapter) kann der Raspberry Pi zur Smarthome-Zentrale erweitert werden. Dadurch kann das Comet Blue das mehr als doppelt so teure Comet DECT ersetzen. Über eine Internetverbindung und den Raspberry Pi kann das Comet Blue von unterwegs aus gesteuert und abgefragt werden.

Heizkörperthermostat mit dem Raspberry Pi und Smartphone steuern

Heizkörperthermostat mit dem Raspberry Pi und Smartphone steuern

Für den Raspberry Pi sollte das Betriebssystem Raspbian Jessie heruntergeladen, auf eine Speicherkarte geschrieben und gestartet werden. Nach dem Start müssen noch die notwendigen Software-Pakete für Bluetooth installiert werden.

# install bluetooth and the tools
sudo bash
apt-get install bluez bluetooth

Mit dem Tool hciconfig läßt sich der Bluetooth-Adapter ein- beziehungsweise ausschalten und mit dem hcitool nach Bluetooth-Smart-Geräten suchen.

# check that bluetooth is not blocked
rfkill list bluetooth

# list bluetooth devices
hciconfig -a

# power on bluetooth device 1 - for Raspberry Pi 1 and 2 you need a USB bluetooth stick
hciconfig hci0 up

# scan for bluetooth low energy devices
hcitool lescan
E0:E5:CF:C1:D4:3F Comet Blue

Das gatttool kann jetzt eine Verbindung zum Heizkörperthermostat herstellen und Kommandos zur Temperatur-Abfrage und Temperatur-Veränderung übertragen.

# send interactive commands to the bluetooth device (Comet Blue)
gatttool -I

# connect
connect E0:E5:CF:C1:D4:3F

# write PIN to authenticate
char-write-req 0x0047 00000000

# PIN 123456 would be 0x1e240:
# char-write-req 0x0047 40e20100

# read the temperatures
char-read-hnd 0x003f

Characteristic value/descriptor: 2d 2a 24 2a 00 04 0a
                                 |  |  |  |  |  |  window open minutes
                                 |  |  |  |  |  window open detection
                                 |  |  |  |  offset temperature
                                 |  |  |  target temperature high (0x2a == 21.0 °C)
                                 |  |  target temperature low (0x24 == 18.0 °C)
                                 |  temperature for manual mode (*2)
                                 current temperature (0x2d == 22.5 °C)

# write the new temperature, for unused values 0x80 is used
char-write-req 0x003f 8080242a008080
                          | | |
                          | | offset temperature
                          | target temperature high (0x2a == 21.0 °C)
                          target temperature low (0x24 == 18.0 °C)

# temperature conversion in bash, example 0x2d == 22.5 °C:
echo "scale=1;$((16#2d)) / 2" | bc
22.5

# in case the basic calculator is not installed
sudo apt-get install bc

Die PIN wird dabei hexadezimal kodiert und umgekehrt übertragen (little endian). Beispiel: die PIN 123456 ist hexadezimal 0x1e240. Als little endian (umgekehrte Übertragung) und mit Nullen auf 4 Byte aufgefüllt ergibt sich: 0x40e20100. Das erste Byte der Temperatur-Steuerung ist die aktuell gemessene Temperatur am Heizkörperthermostat. Diese Temperatur kann im Vergleich zur Raumtemperatur um einige Grad abweichen, da sich das Thermostat direkt an der Heizung befindet. Alle Temperaturwerte müssen durch 2 geteilt werden - Beispiel: 0x2d ist 45 dezimal - geteilt durch 2 = 22,5°C. Das vierte Byte ist die zu erreichende Zieltemperatur für die eingestellte Heizperiode. Das dritte Byte ist die zu erreichende Zieltemperatur für die eingestellte Sparperiode (z.B. die Nachttemperatur). Das 5. Byte ist der Temperatur-Offset.

Die oben gezeigten Kommandos lassen sich durch ein sogennantes "expect"-Skript automatisieren. Wenn der Raspberry Pi mit dem Internet verbunden ist, dann könnte dadurch die Heizung von unterwegs per Smartphone auf eine gewünschte Temperatur eingestellt werden. Über ein Smartphone mit Positionsbestimmung (GPS) ließe sich dadurch auch die Temperatur automatisch auf dem Heimweg hochdrehen.

Automatisierung mit Expect-Skript

Mit dem folgenden Expect-Skript läßt sich die Temperatur über den Raspberry Pi lesen und schreiben.

# install the expect package
sudo apt-get install expect

# download the expect script and make it executable
cd /home/pi
wget http://torsten-traenkner.de/wissen/smarthome/heaterControl.exp
chmod 755 heaterControl.exp

echo "alias h='sudo /home/pi/heaterControl.exp E0:E5:CF:C1:D4:3F 40e20100'" >> /home/pi/.bashrc

# run with:
sudo /home/pi/heaterControl.exp bluetooth_address PIN [new temperature]

# for example:
sudo /home/pi/heaterControl.exp E0:E5:CF:C1:D4:3F 40e20100 20.5

# or short version with the alias:
h 21

Das Listing des Expect-Scripts befindet sich unten auf der Seite.

Trouble-Shooting

Sollte folgende Fehlermeldung beim Verbindungsversuch mit dem Heizkörperthermostat auftauchen:

Attempting to connect to E0:E5:CF:C1:D4:3F
Error: connect error: Transport endpoint is not connected (107)

dann könnte es daran liegen, dass bereits eine Bluetooth-Verbindung besteht - zum Beispiel mit dem Smartphone. Dann muss die parallele Verbindung beendet werden. Sollte das nicht helfen, dann sollte der Raspberry Pi zum Testen näher an das Thermostat gestellt werden. Sollte das auch nicht helfen, müssen die Batterien aus dem Comet Blue noch einmal kurz herausgenommen und wieder eingesetzt werden. Wenn das Problem am Abstand des Raspberry Pi zum Thermostat liegen sollte, so kann ein USB-Stick mit Bluetooth Power Class 1 benutzt werden (z.B. Logilink BT0015).

Ausblick

Das Heizkörperthermostat Comet Blue ist aus meiner Sicht mit Stand vom Januar 2016 noch nicht ausgereift. Das zeigt auch die Firmware-Bezeichnung "0.0.5-beta1". Fehlfunktionen, die zu einem gehäuften Öffnen und Schließen des Thermostats führen, die Lautstärke des Motors und eine bisher fehlende Update-Funktion sind Kinderkrankheiten, die in Zukunft behoben werden müssen. Meine Empfehlung im Augenblick: mit dem Kauf abwarten bis zum nächsten Winter. Eine Alternative könnte das Thermostat eQ-3 Bluetooth sein, das ich allerdings noch nicht getestet habe.

Martin Heckenbach hat mir im Kommentar geschrieben, dass er ein Expect-Skript für das Heizkörperthermostat eQ-3 Bluetooth geschrieben hat:
https://github.com/Heckie75/eQ-3-radiator-thermostat
Vielen Dank für den Beitrag.

Hintergrundinformationen zum Reverse Engineering

Die folgenden Informationen schreibe ich für Interessierte, die wissen möchten, wie die Funkdaten aufgezeichnet werden können. Zur Benutzung des Thermostats sind die folgenden Abschnitte nicht relevant. Das Funkprotokoll des Thermostats kann auf dem Smartphone aufgezeichnet werden. Dazu muss unter dem Menüpunkt Einstellungen - Entwickleroptionen - "Bluetooth HCI-Snoop-Protokoll.." ausgewählt werden.

HCI Snoop unter Android oder CyanogenMod

HCI Snoop unter Android oder CyanogenMod

Die aufgezeichneten Daten können mit dem Programm Android Debug Bridge (adb) über USB-Kabel vom Smartphone auf ein Laptop kopiert und anschließend mit Wireshark (z.B. Version 1.10) analysiert werden.

# on the Laptop type:
adb root

# get bluetooth snoop
adb pull /data/media/0/btsnoop_hci.log

wireshark btsnoop_hci.log
Wireshark mit Bluetooth-Mitschnitt

Wireshark mit Bluetooth-Mitschnitt, handle 0x003f

Handles und UUIDs

Außerdem können über eine Smartphone-App wie Bluetooth Low Energy Explorer (BLExplorer) oder das gatttool auf dem Raspberry Pi die UUIDs beziehungsweise die Handles ausgelesen werden.

# in "gatttool -I" after connect use the following command:

characteristics

# char-read-hnd 0x0003, output: Comet Blue
handle: 0x0002, char properties: 0x02, char value handle: 0x0003, uuid: 00002a00-0000-1000-8000-00805f9b34fb
handle: 0x0004, char properties: 0x02, char value handle: 0x0005, uuid: 00002a01-0000-1000-8000-00805f9b34fb
handle: 0x0006, char properties: 0x0a, char value handle: 0x0007, uuid: 00002a02-0000-1000-8000-00805f9b34fb
handle: 0x0008, char properties: 0x08, char value handle: 0x0009, uuid: 00002a03-0000-1000-8000-00805f9b34fb
handle: 0x000a, char properties: 0x02, char value handle: 0x000b, uuid: 00002a04-0000-1000-8000-00805f9b34fb
handle: 0x000d, char properties: 0x20, char value handle: 0x000e, uuid: 00002a05-0000-1000-8000-00805f9b34fb
handle: 0x0011, char properties: 0x02, char value handle: 0x0012, uuid: 00002a23-0000-1000-8000-00805f9b34fb

# output: Comet Blue
handle: 0x0013, char properties: 0x02, char value handle: 0x0014, uuid: 00002a24-0000-1000-8000-00805f9b34fb

# something like a version string: COBL0126
handle: 0x0015, char properties: 0x02, char value handle: 0x0016, uuid: 00002a26-0000-1000-8000-00805f9b34fb

# firmware version: e.g. 0.0.6-sygonix1
handle: 0x0017, char properties: 0x02, char value handle: 0x0018, uuid: 00002a28-0000-1000-8000-00805f9b34fb

# output: EUROtronic GmbH
handle: 0x0019, char properties: 0x02, char value handle: 0x001a, uuid: 00002a29-0000-1000-8000-00805f9b34fb

# Time and date e.g. 17:51, 13.03.2016
# char-read-hnd 0x001d
# Characteristic value/descriptor: 33 11 0d 03 10
                                   |  |  |  |  |
                                   |  |  |  |  year
                                   |  |  |  month
                                   |  |  day of month
                                   |  hour
                                   minutes
# Monday
handle: 0x001e, char properties: 0x0a, char value handle: 0x001f, uuid: 47e9ee10-47e9-11e4-8939-164230d1df67
# Tuesday
handle: 0x0020, char properties: 0x0a, char value handle: 0x0021, uuid: 47e9ee11-47e9-11e4-8939-164230d1df67
# Wednesday
handle: 0x0022, char properties: 0x0a, char value handle: 0x0023, uuid: 47e9ee12-47e9-11e4-8939-164230d1df67
# Thursday
handle: 0x0024, char properties: 0x0a, char value handle: 0x0025, uuid: 47e9ee13-47e9-11e4-8939-164230d1df67
# Friday
handle: 0x0026, char properties: 0x0a, char value handle: 0x0027, uuid: 47e9ee14-47e9-11e4-8939-164230d1df67
# Saturday
handle: 0x0028, char properties: 0x0a, char value handle: 0x0029, uuid: 47e9ee15-47e9-11e4-8939-164230d1df67
# Sunday
handle: 0x002a, char properties: 0x0a, char value handle: 0x002b, uuid: 47e9ee16-47e9-11e4-8939-164230d1df67

# example monday, times in minutes * 10:
char-read-hnd 0x001f
Characteristic value/descriptor: 6c 8f 00 33 00 00 36 37
                                 |  |  |  |  |  |  |  |
                                 |  |  |  |  |  |  |  end 4 - 0x37 = 55, 55 : 6 = 09:10
                                 |  |  |  |  |  |  start 4  - 0x36 = 54, 54 : 6 = 09:00
                                 |  |  |  |  |  end 3 - 0:00
                                 |  |  |  |  start 3  - 0:00
                                 |  |  |  end 2 - 0x33 = 51, 51 : 6 = 08:30
                                 |  |  start 2  - 0x00 =  0,  0 : 6 = 00:00
                                 |  end 1 - 0x8f = 143, 143 : 6 = 23:50
                                 start 1  - 0x6c = 108, 108 : 6 = 18:00

# holiday 1
handle: 0x002c, char properties: 0x0a, char value handle: 0x002d, uuid: 47e9ee20-47e9-11e4-8939-164230d1df67
# holiday 2
handle: 0x002e, char properties: 0x0a, char value handle: 0x002f, uuid: 47e9ee21-47e9-11e4-8939-164230d1df67
# holiday 3
handle: 0x0030, char properties: 0x0a, char value handle: 0x0031, uuid: 47e9ee22-47e9-11e4-8939-164230d1df67
# holiday 4
handle: 0x0032, char properties: 0x0a, char value handle: 0x0033, uuid: 47e9ee23-47e9-11e4-8939-164230d1df67
# holiday 5
handle: 0x0034, char properties: 0x0a, char value handle: 0x0035, uuid: 47e9ee24-47e9-11e4-8939-164230d1df67
# holiday 6
handle: 0x0036, char properties: 0x0a, char value handle: 0x0037, uuid: 47e9ee25-47e9-11e4-8939-164230d1df67
# holiday 7
handle: 0x0038, char properties: 0x0a, char value handle: 0x0039, uuid: 47e9ee26-47e9-11e4-8939-164230d1df67
# holiday 8
handle: 0x003a, char properties: 0x0a, char value handle: 0x003b, uuid: 47e9ee27-47e9-11e4-8939-164230d1df67

# example holiday 1:
# 04.03.2016 02:00
# 05.03.2016 05:00
# 11°C
char-read-hnd 0x002d
Characteristic value/descriptor: 02 04 03 10 05 05 03 10 16
                                 |  |  |  |  |  |  |  |  temperature * 2
                                 |  |  |  |  |  |  |  year stop
                                 |  |  |  |  |  |  month stop
                                 |  |  |  |  |  day stop
                                 |  |  |  |  clock stop
                                 |  |  |  year
                                 |  |  month
                                 |  day
                                 clock

handle: 0x003c, char properties: 0x0a, char value handle: 0x003d, uuid: 47e9ee2a-47e9-11e4-8939-164230d1df67

# temperatures
handle: 0x003e, char properties: 0x0a, char value handle: 0x003f, uuid: 47e9ee2b-47e9-11e4-8939-164230d1df67

# battery
handle: 0x0040, char properties: 0x0a, char value handle: 0x0041, uuid: 47e9ee2c-47e9-11e4-8939-164230d1df67

# something like a version string: COBL0126
handle: 0x0042, char properties: 0x0a, char value handle: 0x0043, uuid: 47e9ee2d-47e9-11e4-8939-164230d1df67
handle: 0x0044, char properties: 0x0a, char value handle: 0x0045, uuid: 47e9ee2e-47e9-11e4-8939-164230d1df67

# password / PIN
handle: 0x0046, char properties: 0x08, char value handle: 0x0047, uuid: 47e9ee30-47e9-11e4-8939-164230d1df67

Decompilieren

Zusätzlich gibt es noch die "klassische" Möglichkeit des Decompilierens einer App. Leider sind in der App nicht alle Funktionen implementiert, die das Thermostat zur Verfügung stellt. Deshalb lassen sich einige Handles nur mit "Trial and Error" ausprobieren.

# on a Linux computer
mkdir decompile
cd decompile

# download a converter between dex and jar files
wget "http://downloads.sourceforge.net/project/dex2jar/dex2jar-2.0.zip?use_mirror=freefr" -O dex2jar-2.0.zip
unzip dex2jar-2.0.zip
cd dex2jar-2.0/
chmod 755 *sh

mkdir extracted
cd extracted

# download the apk file

# extract the apk file
unzip app.apk

# convert the classes.dex to converted_app.jar
../d2j-dex2jar.sh -f -o converted_app.jar classes.dex

# decompile with "Java Decompiler" GUI
wget "https://github.com/java-decompiler/jd-gui/releases/download/v1.4.0/jd-gui-1.4.0.jar"
java -jar jd-gui-1.4.0.jar converted_app.jar

Listing des Expect-Skripts

Listing des Expect-Skripts heaterControl.exp

#!/usr/bin/expect -f

# author: Torsten Tränkner
# version: 0.1
# license: GPLv3

if { $argc < 2 } {
  puts "Please add a bluetooth address as first parameter, PIN as second parameter and new temperature as third parameter."
  puts "For example:"
  puts "$argv0 E0:E5:CF:C1:D4:3F 40e20100"
  puts "$argv0 E0:E5:CF:C1:D4:3F 40e20100 20.5"
  exit 1
}

set bluetooth_address [lindex $argv 0]
set PIN [lindex $argv 1]

set temperature_string ""

if { $argc > 2 } {
  set normal_temperature [lindex $argv 2]
  set temperature [expr { int($normal_temperature * 2) }]

  set temperature_string 80[string repeat [format "%02x" $temperature] 3]008080
  puts "\nTrying to write temperature: char-write-req 0x003f $temperature_string\n"
}


# wait some seconds for a response
set timeout 15

set counter 0

while {1} {
  exec hciconfig hci0 up

  # start gatttool
  spawn -noecho gatttool -I -b $bluetooth_address

  expect "LE"

  send "connect\n"

  expect {
    "Connection successful" {
      puts "Bluetooth connection established"
      break
    }
    timeout {
      puts "Bluetooth connection timed out";
      send "exit\n"
      expect " "
      exec hciconfig hci0 down

      incr counter
      if {$counter > 3} {
        puts "Giving up"
        exit 2
      }
    }
  }

}

# set a shorter timeout now
set timeout 5

send "char-write-req 0x0047 $PIN\n"
expect {

  "Characteristic value was written successfully" {
    puts "\nThe PIN was accepted.\n"
  }

  "Characteristic Write Request failed:" {
    puts "\nThe provided PIN seems to be wrong"
    send "disconnect\n"
    send "exit\n"
    expect " "
    exec hciconfig hci0 down
    exit 3
  }
}

send "char-read-hnd 0x003f\n"
expect -re "Characteristic value/descriptor.*"

set temperature_value $expect_out(buffer)
set startIndex [string first descriptor: $temperature_value]
set current_temperature_string 0x[string range $temperature_value [expr {$startIndex + 12}] [expr {$startIndex + 13}]]
set old_temperature_string 0x[string range $temperature_value [expr {$startIndex + 15}] [expr {$startIndex + 16}]]

set current_temperature [expr {$current_temperature_string / 2.0}]
set old_temperature [expr {$old_temperature_string / 2.0}]

puts "\n\n===== Current temperature: $current_temperature°C ====="
puts "===== Old set temperature: $old_temperature°C =====\n"

if { $temperature_string ne "" } {
  send "char-write-req 0x003f $temperature_string\n"

  expect "Characteristic value was written successfully" {}
}

send "disconnect\n"
expect "LE"

send "exit\n"
expect " "

puts "\nSetting bluetooth interface hci0 down."
exec hciconfig hci0 down

[Update 21.02.2016]: Artikel um zusätzliche Informationen zum Reverse Engineering ergänzt.

Letzte Aktualisierung am 02. März 2016

Falls noch etwas unklar sein sollte, dann schreib einen Kommentar:

Kommentar schreiben