Hallo, gibt es eine App für das Verbinden eines Polar 9 oder H 10 Brustgurt mit dem Homey?
hey…
danke dir…![]()
Ich habe da vor einiger Zeit auch schon erfolglos gesucht. (Ziel war die herzfrequenzgesteuerte Ventilatorsteuerung eines Ventilators beim Cardiotraining)
Zwei Lösungsansätze habe ich gefunden, aus Zeitmangel und technischer Inkompetenz aber noch nicht umgesetzt:
Einen ESP32 in HA als Bluetooth Gerät flashen und die Daten vom Pulsgurt per MQTT übertragen. Dann kann man sie entweder mit Zigbee2mqtt oder der HA Community App in Homey importieren. Beschreibungen dazu sind leicht zu finden.
Beim Bluetooth Dongle von Shelly habe ich ein Script gefunden das ebenfalls Bluetooth Daten zu MQTT übertragen kann. Das wäre natürlich ohne Löt und Bastelorgie wesentlich eleganter.
Da ich aber erst zum Herbst hin wieder Indoor trainieren werde hat die Umsetzung aktuell keine Priorität.
Wenn du das Projekt aufgreifen möchtest wäre ich fürs anschließende teilen natürlich dankbar.
hello…![]()
ich wollte auch hier nicht zu tief einsteigen.wenn es einfach zu bewerkstelligen gewesen wäre, hätte ich das genutzt (wenn es so gegangen wäre): wir gehen oft im Wald laufen…wenn der puls durch die decke schießen würde, quasi als Notruf…
also hat sich für mich erledigt…
übrigens: Miele hat geantwortet. ich hab aber Topicsauberkeit gelernt…und poste dementsprechend…![]()
LG.
Deine gewünschte Funktion hat mit dem Homey wenig zu tun. Dafür gibt es hunderte Laufapps wo man den Pulsbereich vorgeben kann, der bei über- und unterschreiten des eingestellten Wertes einen Signalton abgibt oder vibriert. Das eine App den Homey anruft oder den RTW bestellt gibt es vermutlich nicht.
Du musst dich schon im Funkbereich vom Homey aufhalten, um Herzfrequenzdaten aus zu werten.
Miele sehe ich mir an, bin gespannt.
danke für den Hinweis…![]()
Ich grätsche da mal dazwischen. Wer bei Homey den Home Assistant als Zuspieler verwendet, kann durchaus mit seiner Herzfrequenz spielen.
Macht aber bitte keinen Blödsinn und verlasst euch nicht auf sowas. Wiederum kann das auch ganz phantastisch funktionieren. Ich bin Diabetiker und lasse von Homey meine Blutzucker-Werte loggen, bzw. mich warnen, wenn es aus dem Ruder läuft. Ich hab das so weit getrieben, dass mir anhand der Werte die Insulin-Dosis empfohlen wird und er mich an die Einnahme der Medikamente erinnert. Ich lebe noch, also macht Homey das prima ! ![]()
hello…
vielen dank… wie gesagt, war es nur als kleine Spielerei gedacht. wie du weisst, bin ich Home Assistant leider raus…![]()
Hab ich das richtig auf dem Schirm? HA war doch mit diesem Raspberry, oder?
Ja, es gibt aber auch noch andere Möglichkeiten.
bin grad am einlesen…![]()
lach…wenn ich schon lese proxmox…und andere Fremdwörter…![]()
ist beides (Homey und HA) zu nutzen für einen newbie sinnvoll? die meisten Automatisierungen laufen ja…
Für gewisse Sachen macht es durchaus Sinn einen Homey und HA parallel laufen zu haben, habe ich auch. Aber ohne Dir auf den Schlips treten zu wollen, arbeite Dich erst mal in Deinen Homey ein und dann können wir in 1-2 Jahren eventuell noch mal über HA reden.
Viele Sachen sind in HA zum Teil deutlich komplizierter als mit einem Homey, angefangen mit dem Installieren von Integrationen, das ist die Bezeichnung für “Apps”, welche nicht direkt in der Standardauswahl gelistet sind.
Du hast vollkommen recht![]()
Ich habe mir die Polar App mal runtergeladen. Ohne den Sensor komme ich im Moment nicht weiter (aktuell Wahoo). Was ich festgestellt habe: man braucht ein Polarkonto
. Allerdings soll HA den Pulsmesser dann über Bluetooth finden. Wie geht das? Mein HA Rechner ist im Serverschrank im Keller, lediglich den ZWave und Zigbeecoordinator habe ich mit usb bzw. Patchkabel an einen besseren Empfangsort verlegt. Wie komme ich an Bluetooth?
Die von mir oben beschriebene Shelly Dongle Möglichkeit fällt aus da Shelly kein Bluetooth GATT kann, also keine Daten. Daher habe ich mal einen ESP32 bestellt. (Das letzte mal das ich irgendwelche Chips geflasht habe war zu 386er Zeiten)
@undertaker Keine Sorge, ich werde mein Leben nicht an ein 10€ Chinachip hängen, ist nur zur Komfortsteuerung beim Rollentraining. Das mit der Warnmeldung wie von Brendi angesprochen nehme ich aber auf, beim HIIT Training übertreibe ich es manchmal.
Oha…das war mal wieder ein ganz großer Post von mir…es sollte doch nur eine Spielerei sein…![]()
Ganz so schlimm ist es nicht. Ich hatte das schon mal vor ein paar Monaten angefangen aber mangels Zeit und beginnender Outdoor Saison wieder fallen lassen. Dank der Anregungen hier habe ich den notwendigen Input um es zu bauen. Ich poste wenn es fertig.
Danke schön…
Es funktioniert wie ich oben geschrieben habe. Zwei Hauptschrite sind notwendig:
- Einen ESP32 mit Arduino flashen, so dass er die Herzfrequenz (und noch ein paar Leistungsdaten für das Kickr Bike, brauchst du nicht) als Gerät ausgibt. Es wird ein MQTT Sensor mit entsprechenden Entitäten erstellt.
Hier die Datei für die Arduino Ide:
#include <WiFi.h>
#include <PubSubClient.h>
#include <NimBLEDevice.h>// ================= CONFIG =================
const char* ssid = “DEIN_WLAN”;
const char* password = “DEIN_WLAN_PASS”;
const char* mqtt_server = “192.168.178.50”;
const char* mqtt_user = “DEIN_USER”;
const char* mqtt_pass = “DEIN_PASSWORT”;// ================= MQTT =================
WiFiClient espClient;
PubSubClient mqtt(espClient);// ================= BLE UUIDs =================
static NimBLEUUID ftmsServiceUUID(“1826”);
static NimBLEUUID indoorBikeUUID(“2AD2”);
static NimBLEUUID hrServiceUUID(“180D”);
static NimBLEUUID hrCharUUID(“2A37”);// ================= DEVICES =================
NimBLEAddress kickrAddress;
NimBLEAddress tickrAddress;
bool kickrFound = false;
bool tickrFound = false;NimBLEClient* kickrClient = nullptr;
NimBLEClient* hrClient = nullptr;// ================= STATE =================
bool kickrConnected = false;
bool hrConnected = false;
bool discoverySent = false;// ================= VALUES =================
volatile float power = 0;
volatile float cadence = 0;
volatile float hr = 0;// Speed wird aus Cadence geschätzt (KICKR sendet kein Speed-Flag)
// Annahme: 700c Rad, 50/11 Übersetzung ~ 8.5m pro Kurbelumdrehung
// speed [km/h] = cadence [rpm] * 8.5 * 60 / 1000
volatile float speed = 0;// ================= WATCHDOG =================
volatile unsigned long lastKickrData = 0;
volatile unsigned long lastHRData = 0;// ================= WIFI =================
void setupWiFi() {
Serial.println(“[WIFI] Verbinde…”);
WiFi.begin(ssid, password);
while (WiFi.status() != WL_CONNECTED) delay(300);
Serial.print("[WIFI] Verbunden, IP: ");
Serial.println(WiFi.localIP());
}// ================= DISCOVERY =================
// FIX: Alle Entitäten mit identischem device-Block und manufacturer/model
// damit HA sie sicher einem Gerät zuordnet.
void sendDiscovery() {
Serial.println(“[MQTT] Sende HA Discovery…”);// Gerätblock als eigene Variable – MUSS in allen Entitäten identisch sein
const char* dev = ““device”:{“identifiers”:[“esp32_trainer”],”
““name”:“ESP32 Trainer”,”
““manufacturer”:“DIY”,”
““model”:“KICKR+TICKR Bridge”}”;char buf[512];
// Power
snprintf(buf, sizeof(buf),
“{“name”:“Leistung”,”
““state_topic”:“trainer/live”,”
““value_template”:”{{ value_json.power | round(0) }}“,”
““unit_of_measurement”:“W”,”
““device_class”:“power”,”
““unique_id”:“esp32_trainer_power”,”
“%s}”, dev);
mqtt.publish(“homeassistant/sensor/esp32_trainer_power/config”, buf, true);// Cadence
snprintf(buf, sizeof(buf),
“{“name”:“Trittfrequenz”,”
““state_topic”:“trainer/live”,”
““value_template”:”{{ value_json.cadence | round(0) }}“,”
““unit_of_measurement”:“rpm”,”
““icon”:“mdi:bike”,”
““unique_id”:“esp32_trainer_cadence”,”
“%s}”, dev);
mqtt.publish(“homeassistant/sensor/esp32_trainer_cadence/config”, buf, true);// Speed
snprintf(buf, sizeof(buf),
“{“name”:“Geschwindigkeit”,”
““state_topic”:“trainer/live”,”
““value_template”:”{{ value_json.speed | round(1) }}“,”
““unit_of_measurement”:“km/h”,”
““device_class”:“speed”,”
““unique_id”:“esp32_trainer_speed”,”
“%s}”, dev);
mqtt.publish(“homeassistant/sensor/esp32_trainer_speed/config”, buf, true);// Heart Rate
snprintf(buf, sizeof(buf),
“{“name”:“Herzfrequenz”,”
““state_topic”:“trainer/live”,”
““value_template”:”{{ value_json.hr | round(0) }}“,”
““unit_of_measurement”:“bpm”,”
““icon”:“mdi:heart-pulse”,”
““unique_id”:“esp32_trainer_hr”,”
“%s}”, dev);
mqtt.publish(“homeassistant/sensor/esp32_trainer_hr/config”, buf, true);Serial.println(“[MQTT] Discovery gesendet.”);
}// ================= MQTT =================
void ensureMQTT() {
if (mqtt.connected()) return;
Serial.println(“[MQTT] Verbinde…”);
unsigned long t = millis();
while (!mqtt.connected() && millis() - t < 5000) {
if (mqtt.connect(“esp32_trainer”, mqtt_user, mqtt_pass)) {
Serial.println(“[MQTT] Verbunden!”);
// Discovery immer neu senden nach Reconnect
sendDiscovery();
discoverySent = true;
} else {
Serial.print(“[MQTT] Fehler rc=”);
Serial.println(mqtt.state());
delay(500);
}
}
}// ================= PUBLISH =================
void publishData() {
if (!mqtt.connected()) return;
char msg[180];
snprintf(msg, sizeof(msg),
“{“power”:%.0f,“cadence”:%.0f,“speed”:%.1f,“hr”:%.0f}”,
(float)power, (float)cadence, (float)speed, (float)hr);
mqtt.publish(“trainer/live”, msg);
}// ================= SCAN =================
class ScanCB : public NimBLEScanCallbacks {
void onResult(const NimBLEAdvertisedDevice* dev) override {
std::string name = dev->getName();
Serial.print(“[SCAN] Gerät: '”);
Serial.print(name.c_str());
Serial.print("’ ");
Serial.println(dev->getAddress().toString().c_str());if (!kickrFound && name.find("KICKR") != std::string::npos) { kickrAddress = dev->getAddress(); kickrFound = true; Serial.println("[SCAN] >>> KICKR gefunden!"); } if (!tickrFound && name.find("TICKR") != std::string::npos) { tickrAddress = dev->getAddress(); tickrFound = true; Serial.println("[SCAN] >>> TICKR gefunden!"); } if (kickrFound && tickrFound) NimBLEDevice::getScan()->stop();}
};// ================= KICKR =================
void connectKickr() {
if (!kickrFound || kickrConnected) return;
Serial.println(“[KICKR] Verbinde…”);if (!kickrClient) kickrClient = NimBLEDevice::createClient();
if (!kickrClient->connect(kickrAddress)) {
Serial.println(“[KICKR] Verbindung fehlgeschlagen!”);
return;
}
Serial.println(“[KICKR] Verbunden!”);auto svc = kickrClient->getService(ftmsServiceUUID);
if (!svc) { Serial.println(“[KICKR] FTMS Service nicht gefunden!”); kickrClient->disconnect(); return; }auto chr = svc->getCharacteristic(indoorBikeUUID);
if (!chr) { Serial.println(“[KICKR] Indoor Bike Char nicht gefunden!”); kickrClient->disconnect(); return; }
if (!chr->canNotify()) { Serial.println(“[KICKR] Kein Notify!”); kickrClient->disconnect(); return; }chr->subscribe(true, (NimBLERemoteCharacteristic*, uint8_t* data, size_t len, bool) {
lastKickrData = millis();
if (len < 4) return;uint16_t flags = data[0] | (data[1] << 8); size_t i = 2; float newCad = cadence; float newPower = power; float newSpeed = 0; // FIX: Default 0, nicht alter Wert – sonst bleibt Speed stehen // Bit 0: Instantaneous Speed (0.01 km/h) if (flags & 0x01) { if (i + 1 >= len) return; uint16_t v = data[i] | (data[i+1] << 8); newSpeed = v / 100.0f; i += 2; } // Bit 1: Average Speed – überspringen if (flags & 0x02) { i += 2; } // Bit 2: Instantaneous Cadence // KICKR Bike Original: empirisch ermittelter Divisor 13.5 if (flags & 0x04) { if (i + 1 >= len) return; uint16_t v = data[i] | (data[i+1] << 8); newCad = v / 13.5f; i += 2; } // Bit 3: Average Cadence – überspringen if (flags & 0x08) { i += 2; } // Bit 4: Total Distance (3 Bytes) – überspringen if (flags & 0x10) { i += 3; } // Bit 5: Resistance Level – überspringen if (flags & 0x20) { i += 2; } // Bit 6: Instantaneous Power // KICKR Bike Original: empirisch ermittelter Divisor 3.5 if (flags & 0x40) { if (i + 1 >= len) return; int16_t v = (int16_t)(data[i] | (data[i+1] << 8)); if (v >= 0 && v <= 7000) newPower = (float)v / 3.5f; i += 2; } // Smoothing: Power leicht glätten, Cadence direkt power = (power * 0.3f) + (newPower * 0.7f); cadence = newCad; // Speed aus Power berechnen (physikalisch: 80kg Fahrer, flach, kein Wind) // P = (CdA*rho/2*v² + Crr*m*g) * v → Näherung: v = (P/2.5)^(1/3) * 3.6 speed = (power > 1.0f) ? powf(power / 2.5f, 1.0f / 3.0f) * 3.6f : 0.0f;});
kickrConnected = true;
lastKickrData = millis();
Serial.println(“[KICKR] Subscribe OK.”);
}// ================= HR =================
void connectHR() {
if (!tickrFound || hrConnected) return;
Serial.println(“[HR] Verbinde…”);if (!hrClient) hrClient = NimBLEDevice::createClient();
if (!hrClient->connect(tickrAddress)) {
Serial.println(“[HR] Verbindung fehlgeschlagen!”);
return;
}
Serial.println(“[HR] Verbunden!”);auto svc = hrClient->getService(hrServiceUUID);
if (!svc) { Serial.println(“[HR] Service nicht gefunden!”); hrClient->disconnect(); return; }auto chr = svc->getCharacteristic(hrCharUUID);
if (!chr) { Serial.println(“[HR] Characteristic nicht gefunden!”); hrClient->disconnect(); return; }
if (!chr->canNotify()) { Serial.println(“[HR] Kein Notify!”); hrClient->disconnect(); return; }chr->subscribe(true, (NimBLERemoteCharacteristic*, uint8_t* data, size_t len, bool) {
lastHRData = millis();
if (len < 2) return;
hr = (data[0] & 0x01) ? (float)(data[1] | (data[2] << 8)) : (float)data[1];
});hrConnected = true;
lastHRData = millis();
Serial.println(“[HR] Subscribe OK.”);
}// ================= SETUP =================
void setup() {
Serial.begin(115200);
delay(1000);
Serial.println(“\n========== START ==========”);setupWiFi();
mqtt.setServer(mqtt_server, 1883);
mqtt.setBufferSize(512);NimBLEDevice::init(“”);
Serial.println(“[BLE] Starte Scan…”);
NimBLEScan* scan = NimBLEDevice::getScan();
scan->setScanCallbacks(new ScanCB(), false);
scan->setActiveScan(true);
scan->start(0, true);
Serial.println(“[BLE] Scan beendet.”);
Serial.print("[BLE] KICKR gefunden: “); Serial.println(kickrFound ? “JA” : “NEIN”);
Serial.print(”[BLE] TICKR gefunden: "); Serial.println(tickrFound ? “JA” : “NEIN”);
}// ================= LOOP =================
void loop() {
ensureMQTT();
mqtt.loop();if (!kickrConnected) connectKickr();
// HR erst verbinden nachdem KICKR stabil ist (500ms Pause dazwischen)
if (kickrConnected && !hrConnected) {
static unsigned long hrConnectDelay = 0;
if (hrConnectDelay == 0) hrConnectDelay = millis() + 500;
if (millis() > hrConnectDelay) {
connectHR();
hrConnectDelay = 0;
}
}static unsigned long lastPublish = 0;
if (millis() - lastPublish > 500) {
lastPublish = millis();
publishData();
}// Watchdog KICKR
if (kickrConnected && millis() - lastKickrData > 5000) {
Serial.println(“[KICKR] Timeout!”);
kickrConnected = false;
if (kickrClient) { kickrClient->disconnect(); NimBLEDevice::deleteClient(kickrClient); kickrClient = nullptr; }
}// Watchdog HR
if (hrConnected && millis() - lastHRData > 8000) {
Serial.println(“[HR] Timeout!”);
hrConnected = false;
if (hrClient) { hrClient->disconnect(); NimBLEDevice::deleteClient(hrClient); hrClient = nullptr; }
}delay(200);
}
2. In Homey mit der HA-Community App ein neues Gerät als Sensor importieren. Dann hast Du die Herzfrequenz in Homey.
Ich habe dann die Steuerung des Dyson und der Lampe als Script erstellt, das geht natürlich auch mit einem Flow/Advanced Flow.
Hallo,
cool…vielen dank. Ich werde es versuchen hinzukriegen…
