Glance Clock alternativen?

Hallo zusammen,
ich suche gerade eine Möglichkeit, die Uhrzeit, Tageszeit, Restzeit bis zum Aufstehen irgendwie visuell darzustellen. Das ganze ist für einen zeitblinden Autisten und soll helfen, den Tag und besonders die Schlafzeiten zu koordinieren. Dazu muss es für Erwachsene sein, alles was man so im Netz findet, richtet sich eher an Kinder im Grundschulalter.
Natürlich hab ich meine Suche in ChatGPT begonnen und bin schließlich auf ein Uhr gestoßen, die Glance Clock heißt. Leider scheint das Projekt und der dazugehörige Server bereits aufgegeben zu sein. Auch wenn es eine HACS (siehe Unten) gibt, um den Server zu ersetzen, wäre mir eine voll supportete Lösung lieber. Obwohl es eigentlich genau das richtige gewesen wäre.
Hat jemand noch eine Idee oder ein Produkt zur Hand, das ähnliches bietet?

Schon mal vielen Dank für die Antworten.
Gruß,
Volker

Glance Clock Integration for Home Assistant

Ich verstehe dein Problem nicht. Die Uhr kann man immer noch kaufen. Einen externen Server benötigst du nicht, sobald du die Uhr an HA angebunden hast. Denn dann ist HomeAssistant für die Kommunikation zuständig.

Naja, das sind ja nur die Restbestände, die noch auf Lager liegen.
Ich sag ja nicht, dass ich das völlig ausschließe, aber wenn es etwas vergleichbares aktuelles gäbe, würde ich das schon bevorzugen

Kleines Update, falls es jemand liest.

Die Glance Clock wurde am 28. Juli geliefert, seither versuche ich das Gerät mit HA zu verbinden. Bisher erfolglos.
Diverse Bluetooth Adapter getestet, extra einen ESP32 Bluetooth Proxy angeschafft, der brachte aber auch keinen Erfolg. War die Idee von Gemini…. glaube ich….
Nach einigen Tagen stumpfes an die Anleitung halten, bin ich umgeschwenkt und habe mir Hilfe bei diversen KIs gesucht. hat aber auch nix gebracht.

Bisher eine 90€ teure Plastikscheibe…..

Hallo Volker,

kenne das Problem mit Bluetooth Geräten…hab meine KI mal bemüht, hat mir bei meinem Problem damals auch geholfen
Bevor du deine 90€ in die Tonne haust, ließ dir das durch - vieleicht hilft dir was dabei

-Achtung KI Antwort - wem es nicht gefällt bitte wegschauen :stuck_out_tongue: -


Problem: ESP32 Bluetooth Proxy funktioniert nicht

Ein ESP32 Proxy leitet nur passive BLE-Advertisements weiter. Die Glance Clock erfordert eine aktive, authentifizierte GATT-Verbindung mit PIN-Pairing – das ist über einen Proxy architekturbedingt nicht möglich.

Voraussetzungen für eine funktionierende Einbindung:

  • Home Assistant OS oder Supervised
  • Direkt angebundener Bluetooth-Adapter am HA-Host (kein Proxy) – Raspberry Pi hat den eingebaut
  • Terminal & SSH Add-on installiert
  • Kein Proxmox – USB-Passthrough eines Bluetooth-Adapters in eine Proxmox-VM ist notorisch unzuverlässig. BlueZ (der Linux-Bluetooth-Stack) verliert dabei häufig die Verbindung zum Adapter, das bluetoothctl-Pairing schlägt fehl oder der Adapter wird nach einem Neustart nicht mehr erkannt. Wer HA unter Proxmox betreibt, hat hier strukturell schlechte Karten – ein nativer HA-OS-Host (Raspberry Pi, HA Green/Yellow, dedizierter Mini-PC) ist für diese Integration Pflicht.

Schritt 1 – Factory Reset der Uhr Reset-Taste + Power-Taste gleichzeitig halten → Reset loslassen → Power weiter halten bis Blinkmuster wechselt

Schritt 2 – Pairing via bluetoothctl

bluetoothctl
agent on
default-agent
scan on
pair XX:XX:XX:XX:XX:XX   # MAC der Uhr eintragen
# PIN vom Uhrendisplay eingeben
exit

Schritt 3 – bluetooth-cts Add-on installieren https://github.com/PorlyBe/bluetooth-cts – dauerhaft laufen lassen, sonst keine korrekte Zeitsynchronisation.

Schritt 4 – Integration als Custom Repository in HACS https://github.com/PorlyBe/glance_clock_ha → Kategorie „Integration" → HA neu starten → Gerät wird automatisch erkannt.


Betrieb außerhalb der Bluetooth-Reichweite des HA-Hosts

Ein ESP32 Proxy mit aktivem Verbindungsmodus (active: true in ESPHome) könnte theoretisch funktionieren, ist für diesen Anwendungsfall aber nicht dokumentiert und ungetestet. Zuverlässige Alternative: ein zweiter SBC (z.B. Raspberry Pi Zero 2W) direkt neben der Uhr, der per Netzwerk mit HA kommuniziert – das ist aber kein Proxy mehr sondern ein separater Host.

Nach 2 Wochen try and error, …
Ich habe die Uhr verbunden bekommen und konnte in den vergangenen 5 Tagen einiges Testen.
Leider waren die Segmente der Uhr nicht wirklich adressierbar. Außer “Notify”, bei dem die ganze Uhr in einer gewählten Farbe aufleuchtet und ein vernehmliches “Dingdong” von sich gibt, hat eigentlich kein Befehl funktioniert.
Heute morgen hatte ich noch ein zwei Ideen, die ich testen wollte, da hatte sich die Uhr über Nacht aber wieder getrennt und ließ sich nicht mehr verbinden. Selbst nach mehrfachem Resett. Auch die zur Übertragung der Firmware nötigen “nRF Connect” App konnte sich nicht damit verbinden, obwohl sie verbindungsbereit war.

Das hat dann doch den letzten Ruck gegeben und ich habe die Versuche nun eingestellt, das Ding wieder in seine Verpackung gesteckt und nun geht es zurück an Amazon… leider, weil optisch immer noch die hübscheste Lösung :cry:

Damit wäre die Frage zu einer Alternative wieder offen :upside_down_face:
Nach Möglichkeit möchte ich nicht basteln.

Unten noch das Protokoll, fängt leider nicht ganz zu Beginn an, sondern erst, nachdem ich die Uhr erfolgreich verbunden und den “Notify” Befehl getestet habe.
Natürlich alles mit Unterstützung von Claude (will mich nicht mit KI-Federn schmücken)

Gruß,
Volker

Protokoll

Glance Clock – Zeitpunkte Dashboard: Chatprotokoll & aktueller Stand

Stand: 9. August 2026

Übersicht / Konzept

Dashboard zur Verwaltung von 8 Zeitpunkten/Zeiträumen (Schlafenszeit, Freizeit, Abfahrzeit, 5 frei benennbare), jeweils mit:

Aktiv/Inaktiv-Schalter
Typ: Zeitpunkt (einzelner Alarm) oder Zeitraum (Von–Bis)
Farbe (7 Optionen)
Icon (14 Optionen)
Bei Erreichen: Nachricht an die Glance Clock via glance_clock.send_notice

Wichtige technische Erkenntnis (offener Punkt): Die Glance-Clock-Integration kann laut Code-Analyse keine dauerhafte farbige Ringanzeige über einen Zeitraum anzeigen. glance_clock.send_notice ist ein kurzer Blitz-Hinweis (Text+Farbe+Animation, aber nur für Sekunden). glance_clock.send_timer zeigt einen Countdown (Zahlen runterzählend), aber ohne Farbparameter und der Text wird nur kurz als Ankündigung eingeblendet, nicht dauerhaft.

:right_arrow: Offene Entscheidung: Wie soll bei “Zeitraum”-Typ verfahren werden? Optionen waren: (a) nur kurzer Blitz bei Start+Ende, (b) zusätzlich alle X Minuten während der Dauer erneut blitzen, (c) send_timer als Countdown nutzen (ohne Wunschfarbe), (d) Idee verwerfen, nur Start/Ende reicht. → Noch nicht final entschieden, aktuelle Automation sendet bei “Zeitraum” einen Blitz bei Start UND bei Ende (Variante ähnlich zu a).

Ebenfalls offen: Sound-Schalter (input_boolean.glance_clock_sound_aktiv) wurde vorgeschlagen, aber noch nicht final in Dashboard/Automation eingebaut bestätigt – Code unten ist vorbereitet, aber prüfen ob schon übernommen.

  1. Helpers – /config/packages/zeitpunkte.yaml
    yaml
    input_datetime:
    zeitpunkt_schlafenszeit:
    name: “Schlafenszeit (Von)”
    has_time: true
    has_date: false
    icon: mdi:bed
    zeitpunkt_schlafenszeit_bis:
    name: “Schlafenszeit (Bis)”
    has_time: true
    has_date: false
    icon: mdi:bed
    zeitpunkt_freizeit:
    name: “Freizeit (Von)”
    has_time: true
    has_date: false
    icon: mdi:gamepad-variant
    zeitpunkt_freizeit_bis:
    name: “Freizeit (Bis)”
    has_time: true
    has_date: false
    zeitpunkt_abfahrzeit:
    name: “Abfahrzeit (Von)”
    has_time: true
    has_date: false
    icon: mdi:car
    zeitpunkt_abfahrzeit_bis:
    name: “Abfahrzeit (Bis)”
    has_time: true
    has_date: false
    zeitpunkt_frei_1:
    name: “Frei 1 (Von)”
    has_time: true
    has_date: false
    zeitpunkt_frei_1_bis:
    name: “Frei 1 (Bis)”
    has_time: true
    has_date: false
    zeitpunkt_frei_2:
    name: “Frei 2 (Von)”
    has_time: true
    has_date: false
    zeitpunkt_frei_2_bis:
    name: “Frei 2 (Bis)”
    has_time: true
    has_date: false
    zeitpunkt_frei_3:
    name: “Frei 3 (Von)”
    has_time: true
    has_date: false
    zeitpunkt_frei_3_bis:
    name: “Frei 3 (Bis)”
    has_time: true
    has_date: false
    zeitpunkt_frei_4:
    name: “Frei 4 (Von)”
    has_time: true
    has_date: false
    zeitpunkt_frei_4_bis:
    name: “Frei 4 (Bis)”
    has_time: true
    has_date: false
    zeitpunkt_frei_5:
    name: “Frei 5 (Von)”
    has_time: true
    has_date: false
    zeitpunkt_frei_5_bis:
    name: “Frei 5 (Bis)”
    has_time: true
    has_date: false

input_text:
zeitpunkt_frei_1_name:
name: “Name Frei 1”
initial: “Zeitpunkt 1”
max: 30
zeitpunkt_frei_2_name:
name: “Name Frei 2”
initial: “Zeitpunkt 2”
max: 30
zeitpunkt_frei_3_name:
name: “Name Frei 3”
initial: “Zeitpunkt 3”
max: 30
zeitpunkt_frei_4_name:
name: “Name Frei 4”
initial: “Zeitpunkt 4”
max: 30
zeitpunkt_frei_5_name:
name: “Name Frei 5”
initial: “Zeitpunkt 5”
max: 30

input_boolean:
zeitpunkt_schlafenszeit_aktiv:
name: “Schlafenszeit aktiv”
icon: mdi:toggle-switch
zeitpunkt_freizeit_aktiv:
name: “Freizeit aktiv”
icon: mdi:toggle-switch
zeitpunkt_abfahrzeit_aktiv:
name: “Abfahrzeit aktiv”
icon: mdi:toggle-switch
zeitpunkt_frei_1_aktiv:
name: “Frei 1 aktiv”
icon: mdi:toggle-switch
zeitpunkt_frei_2_aktiv:
name: “Frei 2 aktiv”
icon: mdi:toggle-switch
zeitpunkt_frei_3_aktiv:
name: “Frei 3 aktiv”
icon: mdi:toggle-switch
zeitpunkt_frei_4_aktiv:
name: “Frei 4 aktiv”
icon: mdi:toggle-switch
zeitpunkt_frei_5_aktiv:
name: “Frei 5 aktiv”
icon: mdi:toggle-switch

Vorgeschlagen, Status “noch einzubauen” prüfen:

glance_clock_sound_aktiv:
name: “Glance Clock Sound aktiv”
icon: mdi:volume-high

input_select:
zeitpunkt_schlafenszeit_typ: &typ_select
name: “Typ”
options: [“Zeitpunkt”, “Zeitraum”]
initial: “Zeitpunkt”
zeitpunkt_freizeit_typ: *typ_select
zeitpunkt_abfahrzeit_typ: *typ_select
zeitpunkt_frei_1_typ: *typ_select
zeitpunkt_frei_2_typ: *typ_select
zeitpunkt_frei_3_typ: *typ_select
zeitpunkt_frei_4_typ: *typ_select
zeitpunkt_frei_5_typ: *typ_select

zeitpunkt_schlafenszeit_farbe: &farbe_select
name: “Farbe”
options: [“Weiß”, “Rot”, “Blau”, “Grün”, “Orange”, “Lila”, “Gelb”]
initial: “Blau”
zeitpunkt_freizeit_farbe: *farbe_select
zeitpunkt_abfahrzeit_farbe: *farbe_select
zeitpunkt_frei_1_farbe: *farbe_select
zeitpunkt_frei_2_farbe: *farbe_select
zeitpunkt_frei_3_farbe: *farbe_select
zeitpunkt_frei_4_farbe: *farbe_select
zeitpunkt_frei_5_farbe: *farbe_select

zeitpunkt_schlafenszeit_icon: &icon_select
name: “Icon”
options: [“Bett”, “Controller”, “Auto”, “Wecker”, “Mond”, “Sonne”, “Schule”, “Essen”, “Sport”, “Fernseher”, “Buch”, “Dusche”, “Zähne”, “Stern”]
initial: “Bett”
zeitpunkt_freizeit_icon: *icon_select
zeitpunkt_abfahrzeit_icon: *icon_select
zeitpunkt_frei_1_icon: *icon_select
zeitpunkt_frei_2_icon: *icon_select
zeitpunkt_frei_3_icon: *icon_select
zeitpunkt_frei_4_icon: *icon_select
zeitpunkt_frei_5_icon: *icon_select

Aktivierung in configuration.yaml (bereits vorhanden bei dir):

yaml
homeassistant:
packages: !include_dir_named packages
2. Dashboard-Template – button_card_templates: (ganz oben in der Dashboard-Raw-YAML, vor views:)

Aktueller Stand inkl. korrigiertem state_display (Zeitzonen-Fix) und korrigiertem browser_mod-Popup (JS-Objekt-Fix):

yaml
button_card_templates:
zeitpunkt_kachel:
show_name: true
show_state: true
show_icon: true
size: 40%
styles:
card:
- width: 85px
- height: 85px
- border-radius: 16px
- padding: 6px
- opacity: |
[[[
const aktiv = states[‘input_boolean.’ + variables.entity_prefix + ‘_aktiv’]?.state;
return aktiv === ‘on’ ? ‘1’ : ‘0.35’;
]]]
icon:
- color: |
[[[
const farben = {
“Weiß”: “#FFFFFF”, “Rot”: “#FF3B30”, “Blau”: “#2196F3”,
“Grün”: “#8BC34A”, “Orange”: “#FF8C00”, “Lila”: “#8A2BE2”, “Gelb”: “#CDDC39
};
const farbe = states[‘input_select.’ + variables.entity_prefix + ‘_farbe’]?.state;
return farben[farbe] || ‘#FFFFFF’;
]]]
name:
- font-size: 11px
- font-weight: 500
state:
- font-size: 13px
- font-weight: 600
icon: |
[[[
const icons = {
“Bett”: “mdi:bed”, “Controller”: “mdi:gamepad-variant”, “Auto”: “mdi:car”,
“Wecker”: “mdi:alarm”, “Mond”: “mdi:weather-night”, “Sonne”: “mdi:white-balance-sunny”,
“Schule”: “mdi:school”, “Essen”: “mdi:food”, “Sport”: “mdi:dumbbell”,
“Fernseher”: “mdi:television”, “Buch”: “mdi:book-open-variant”, “Dusche”: “mdi:shower”,
“Zähne”: “mdi:toothbrush”, “Stern”: “mdi:star”
};
const icon = states[‘input_select.’ + variables.entity_prefix + ‘_icon’]?.state;
return icons[icon] || ‘mdi:clock-outline’;
]]]
name: |
[[[
if (variables.fixed_name) return variables.fixed_name;
return states[‘input_text.’ + variables.entity_prefix + ‘_name’]?.state || ‘’;
]]]
state_display: |
[[[
const prefix = variables.entity_prefix;
const typ = states[‘input_select.’ + prefix + ‘_typ’]?.state;
const fmt = (ent) => ent ? ent.state.substring(0,5) : ‘–:–’;
const von = fmt(states[‘input_datetime.’ + prefix]);
if (typ === ‘Zeitraum’) {
const bis = fmt(states[‘input_datetime.’ + prefix + ‘_bis’]);
return von + ‘–’ + bis;
}
return von;
]]]
tap_action:
action: fire-dom-event
browser_mod: |
[[[
const prefix = variables.entity_prefix;
const title = variables.fixed_name || (states[‘input_text.’ + prefix + ‘_name’]?.state || ‘’);
return {
service: ‘browser_mod.popup’,
data: {
title: title,
content: {
type: ‘entities’,
entities: [
{ entity: ‘input_boolean.’ + prefix + ‘_aktiv’, name: ‘Aktiv’ },
{ entity: ‘input_select.’ + prefix + ‘_typ’, name: ‘Typ’ },
{ entity: ‘input_datetime.’ + prefix, name: ‘Von’ },
{ entity: ‘input_datetime.’ + prefix + ‘_bis’, name: ‘Bis (nur bei Zeitraum)’ },
{ entity: ‘input_select.’ + prefix + ‘_farbe’, name: ‘Farbe’ },
{ entity: ‘input_select.’ + prefix + ‘_icon’, name: ‘Icon’ }
]
}
}
};
]]]

Vorgeschlagene Verbesserung (Zeit-Picker-Card statt Textfelder) – noch nicht final bestätigt ob installiert/übernommen:

Benötigt HACS-Installation: time-picker-card von GeorgeSG (Frontend-Kategorie in HACS).

yaml
content:
type: entities
entities:
- entity: input_boolean.[[[ return variables.entity_prefix ]]]_aktiv
name: “Aktiv”
- entity: input_select.[[[ return variables.entity_prefix ]]]_typ
name: “Typ”
- type: custom:time-picker-card
entity: input_datetime.[[[ return variables.entity_prefix ]]]
layout: {name: inside}
- type: custom:time-picker-card
entity: input_datetime.[[[ return variables.entity_prefix ]]]_bis
layout: {name: inside}
- entity: input_select.[[[ return variables.entity_prefix ]]]_farbe
name: “Farbe”
- entity: input_select.[[[ return variables.entity_prefix ]]]_icon
name: “Icon”

(Hinweis: Diese Variante nutzt noch die alte, fehlerhafte Template-Syntax für Entity-IDs innerhalb der entities-Liste, aus einer früheren Nachricht – müsste wie oben beim browser_mod-Fix auf die JS-Objekt-Methode umgestellt werden, falls sie noch verwendet wird. Bitte vor Einsatz prüfen/anpassen.)

  1. Automation 1 – “Zeitpunkte - Trigger Event”

Aktueller, funktionierender Stand (mit korrigiertem event:-Schlüssel statt action: event.fire):

yaml
alias: Zeitpunkte - Trigger Event
triggers:

  • trigger: time
    at: input_datetime.zeitpunkt_schlafenszeit
    id: zeitpunkt_schlafenszeit
  • trigger: time
    at: input_datetime.zeitpunkt_schlafenszeit_bis
    id: zeitpunkt_schlafenszeit_ende
  • trigger: time
    at: input_datetime.zeitpunkt_freizeit
    id: zeitpunkt_freizeit
  • trigger: time
    at: input_datetime.zeitpunkt_freizeit_bis
    id: zeitpunkt_freizeit_ende
  • trigger: time
    at: input_datetime.zeitpunkt_abfahrzeit
    id: zeitpunkt_abfahrzeit
  • trigger: time
    at: input_datetime.zeitpunkt_abfahrzeit_bis
    id: zeitpunkt_abfahrzeit_ende
  • trigger: time
    at: input_datetime.zeitpunkt_frei_1
    id: zeitpunkt_frei_1
  • trigger: time
    at: input_datetime.zeitpunkt_frei_1_bis
    id: zeitpunkt_frei_1_ende
  • trigger: time
    at: input_datetime.zeitpunkt_frei_2
    id: zeitpunkt_frei_2
  • trigger: time
    at: input_datetime.zeitpunkt_frei_2_bis
    id: zeitpunkt_frei_2_ende
  • trigger: time
    at: input_datetime.zeitpunkt_frei_3
    id: zeitpunkt_frei_3
  • trigger: time
    at: input_datetime.zeitpunkt_frei_3_bis
    id: zeitpunkt_frei_3_ende
  • trigger: time
    at: input_datetime.zeitpunkt_frei_4
    id: zeitpunkt_frei_4
  • trigger: time
    at: input_datetime.zeitpunkt_frei_4_bis
    id: zeitpunkt_frei_4_ende
  • trigger: time
    at: input_datetime.zeitpunkt_frei_5
    id: zeitpunkt_frei_5
  • trigger: time
    at: input_datetime.zeitpunkt_frei_5_bis
    id: zeitpunkt_frei_5_ende
    actions:
  • variables:
    is_end: ‘{{ trigger.id.endswith(’‘_ende’‘) }}’
    base: ‘{{ trigger.id[:-5] if trigger.id.endswith(’‘_ende’‘) else trigger.id }}’
  • condition: template
    value_template: |
    {{ is_state(‘input_boolean.’ ~ base ~ ‘_aktiv’, ‘on’)
    and (not is_end or states(‘input_select.’ ~ base ~ ‘_typ’) == ‘Zeitraum’) }}
  • variables:
    zp_name: >
    {% set feste_namen =
    {‘zeitpunkt_schlafenszeit’:‘Schlafenszeit’,‘zeitpunkt_freizeit’:‘Freizeit’,‘zeitpunkt_abfahrzeit’:‘Abfahrzeit’}
    %} {{ feste_namen.get(base, states(‘input_text.’ ~ base ~ ‘_name’)) }}
    zp_farbe: ‘{{ states(’‘input_select.’’ ~ base ~ ‘’_farbe’‘) }}’
    zp_icon: ‘{{ states(’‘input_select.’’ ~ base ~ ‘’_icon’‘) }}’
  • event: zeitpunkt_ausgeloest
    event_data:
    id: ‘{{ base }}’
    phase: ‘{{ ‘‘ende’’ if is_end else ‘‘start’’ }}’
    name: ‘{{ zp_name }}’
    farbe: ‘{{ zp_farbe }}’
    icon: ‘{{ zp_icon }}’
    mode: parallel

Bestätigt funktionierend (Trace erfolgreich durchgelaufen laut Chatverlauf).

  1. Automation 2 – “Zeitpunkt → Glance Clock”

Mit optionalem Sound-Schalter (Status: vorgeschlagen, Übernahme noch bestätigen):

yaml
alias: “Zeitpunkt → Glance Clock”
triggers:

  • trigger: event
    event_type: zeitpunkt_ausgeloest
    actions:
  • action: glance_clock.send_notice
    data:
    text: >-
    {{ trigger.event.data.name }}{{ ’ – Ende’ if trigger.event.data.phase == ‘ende’ else ‘’ }}
    animation: pulse
    sound: >-
    {{ ‘bells’ if is_state(‘input_boolean.glance_clock_sound_aktiv’, ‘on’) else ‘none’ }}
    color: >-
    {{ {‘Weiß’:‘white’,‘Rot’:‘red’,‘Blau’:‘blue’,‘Grün’:‘lime’,
    ‘Orange’:‘dark_orange’,‘Lila’:‘blue_violet’,‘Gelb’:‘lawn_green’}
    [trigger.event.data.farbe] }}
    priority: medium
    mode: parallel

Falls Sound-Schalter noch NICHT eingebaut werden soll, einfach sound: none fest statt der Template-Zeile.

  1. Dashboard – View “Glance Clock” (8 Kacheln im Grid)
    yaml
  • type: masonry
    path: glance-clock
    title: Glance Clock
    icon: far:clock
    cards:
    • type: grid
      columns: 4
      square: false
      cards:
      • type: custom:button-card
        template: zeitpunkt_kachel
        variables: {entity_prefix: zeitpunkt_schlafenszeit, fixed_name: “Schlafenszeit”}
      • type: custom:button-card
        template: zeitpunkt_kachel
        variables: {entity_prefix: zeitpunkt_freizeit, fixed_name: “Freizeit”}
      • type: custom:button-card
        template: zeitpunkt_kachel
        variables: {entity_prefix: zeitpunkt_abfahrzeit, fixed_name: “Abfahrzeit”}
      • type: custom:button-card
        template: zeitpunkt_kachel
        variables: {entity_prefix: zeitpunkt_frei_1}
      • type: custom:button-card
        template: zeitpunkt_kachel
        variables: {entity_prefix: zeitpunkt_frei_2}
      • type: custom:button-card
        template: zeitpunkt_kachel
        variables: {entity_prefix: zeitpunkt_frei_3}
      • type: custom:button-card
        template: zeitpunkt_kachel
        variables: {entity_prefix: zeitpunkt_frei_4}
      • type: custom:button-card
        template: zeitpunkt_kachel
        variables: {entity_prefix: zeitpunkt_frei_5}

Bestätigt: Kacheln sind im Dashboard vorhanden und funktionsfähig (laut Chatverlauf, inkl. Farbe/Icon-Anzeige und Popup zum Bearbeiten).

  1. Erkenntnisse zu glance_clock.send_timer (Testprotokoll)

Getestet mit:

yaml
action: glance_clock.send_timer
data:
countdown: 10
intervals:
- text: “Schlafenszeit”
duration: 12
countdown: 5
final_text: “Test fertig”

Beobachtetes Verhalten auf der Uhr:

countdown (10s) läuft zuerst still ab, ohne Textmeldung
Automatischer Text “Start next” (Firmware-generiert)
Text “Schlafenszeit” erscheint kurz
Countdown-Zahlen von 5 auf 0 (countdown-Wert des Intervals)
Countdown-Zahlen von 12 auf 0 (duration-Wert des Intervals)
(vermutlich danach) final_text “Test fertig”

Schlussfolgerung: send_timer ist ein reiner Kitchen-Timer-Countdown ohne Farbparameter. Text wird nur kurz angekündigt, nicht dauerhaft angezeigt. Für “dauerhafte Farbanzeige während eines Zeitraums” (Wunsch 2) oder “Farbring an echten Ziffernblatt-Positionen” (Wunsch 3) bietet die Integration/Hardware keine passende Funktion.

Offene Entscheidung (zuletzt gestellte Frage, noch unbeantwortet): Wie soll bei Typ “Zeitraum” verfahren werden?

(a) Nur kurzer Blitz bei Start + Ende — aktuell in Automation 2 so umgesetzt
(b) Zusätzlich alle X Minuten während der Dauer erneut kurz aufblitzen
(c) send_timer als Countdown nutzen (zeigt Restzeit als Zahlen, ohne Wunschfarbe/-text dauerhaft)
(d) Idee verwerfen, nur Start/Ende reicht
7. Entscheidung zu Punkt 2/3: Custom-Scene-Feature (EXPERIMENTELL, in Entwicklung)

Entscheidung: Nutzer möchte das größere Entwicklungsprojekt verfolgen — echter farbiger Ring-Balken über die Dauer eines Zeitraums, via CustomScene-Protobuf-Nachricht (MovingBar-Methode), die in der Original-Integration nicht genutzt/exponiert wird.

Grundlage: Analyse der Readme von GitHub - Hypfer/glance-clock: Glance Clock BLE protocol reverse-engineered · GitHub (Reverse-Engineering-Doku der Hardware/Protokoll) zeigt: Die Uhr hat 4× 48-LED-RGB-Ringe und kennt “Scenes”, die (im Gegensatz zum kurzen Notice-Blitz) dauerhaft aktiv bleiben, bis gelöscht. CustomScene.Object.MovingBarData hat Felder area, frontColor, backColor, speed — genau passend für einen farbigen Ring-Abschnitt. lifeTime (Sekunden) sollte die Szene automatisch beenden.

Wichtig: CustomScene ist zwar im Protobuf-Schema (Glance.proto / glance_pb2.py) vollständig definiert, wird aber nirgendwo im originalen Python-Code der Integration verwendet (nur Notice, Timer, ForecastScene, Settings sind implementiert). Das Feature wurde daher komplett neu für diese Integration entwickelt.

7.1 Neuer Code — Status: Installiert, aber Ergebnis noch nicht wie erwartet

a) Neue Methode in notify.py (eingefügt nach async_send_timer):

python
async def async_send_time_range(self, text, color, duration_seconds, area=0, scene_slot=9) → bool:
“”“EXPERIMENTAL: Send a persistent colored ring bar for a given duration.”“”
if not self._connection_manager or not self._connection_manager.is_connected:
_LOGGER.warning(“Device not connected, cannot send time range scene”)
return False

    try:
        from .glance_pb2 import CustomScene, TextData  # type: ignore
        import re

        def text_with_icons_to_bytes(t: str) -> bytes:
            icon_regex = re.compile(r"\[icon:(\d+)\]")
            parts = []
            last_index = 0
            for match in icon_regex.finditer(t):
                for c in t[last_index:match.start()]:
                    parts.append(ord(c) & 0x7F)
                parts.append(int(match.group(1)))
                last_index = match.end()
            for c in t[last_index:]:
                parts.append(ord(c) & 0x7F)
            return bytes(parts)

        scene = CustomScene()
        obj = scene.object.add()
        obj.method = 5  # MovingBar
        obj.lifeTime = int(duration_seconds)

        obj.movingBar.area = int(area)
        obj.movingBar.frontColor = int(color)
        obj.movingBar.backColor = 0  # Black
        obj.movingBar.speed = 0  # static, no movement

        if text:
            text_data = TextData()
            text_data.text = text_with_icons_to_bytes(text)
            obj.text.append(text_data)

        scene_bytes = scene.SerializeToString()
        # CustomScene command: 0,0,<display_mode 24=Ring&Text>,<scene_slot>
        header = bytearray([0, 0, 24, scene_slot])
        command = header + scene_bytes

        _LOGGER.info(
            f"Sending time range scene: text={text}, color={color}, "
            f"duration={duration_seconds}s, area={area}, slot={scene_slot}")
        _LOGGER.debug(f"CustomScene command: {command.hex()}")

        success = await self._connection_manager.send_command(bytes(command))
        if success:
            _LOGGER.info("Time range scene sent successfully")
            return True
        else:
            _LOGGER.error("Failed to send time range scene command")
            return False
    except Exception as e:
        _LOGGER.error(f"Error sending time range scene: {e}")
        import traceback
        _LOGGER.error(f"Full traceback: {traceback.format_exc()}")
        return False

b) Neue Datei services/time_range.py:

python
“”“EXPERIMENTAL: Time range scene service for Glance Clock.”“”
import logging
from homeassistant.core import HomeAssistant, ServiceCall
from homeassistant.config_entries import ConfigEntry

from ..const import DOMAIN, COLORS

_LOGGER = logging.getLogger(name)

async def handle_send_time_range(hass: HomeAssistant, entry: ConfigEntry, call: ServiceCall):
“”“Handle sending a persistent time-range scene to the device.”“”
entry_data = hass.data[DOMAIN][entry.entry_id]
notify_service = hass.data.get(DOMAIN + “_notify”, {}).get(entry.entry_id)
connection_manager = entry_data.get(“connection_manager”)

if notify_service:
    if connection_manager and not hasattr(notify_service, '_connection_manager'):
        notify_service._connection_manager = connection_manager

    text = call.data.get("text", "")
    color_name = call.data.get("color", "white")
    duration_seconds = call.data.get("duration_seconds", 60)
    area = call.data.get("area", 0)
    scene_slot = call.data.get("scene_slot", 9)

    color = COLORS.get(color_name, 12)

    success = await notify_service.async_send_time_range(
        text=text,
        color=color,
        duration_seconds=duration_seconds,
        area=area,
        scene_slot=scene_slot
    )

    if success:
        _LOGGER.info(f"Time range scene sent successfully: {text}")
    else:
        _LOGGER.error(f"Failed to send time range scene: {text}")
else:
    _LOGGER.error("Notification service not found for sending time range scene")

c) Änderungen in services/init.py (bestätigt korrekt eingefügt, per cat verifiziert):

Import: from .time_range import handle_send_time_range
Wrapper: _handle_send_time_range
Registrierung: hass.services.async_register(DOMAIN, “send_time_range”, _handle_send_time_range)

d) Ergänzung services.yaml:

yaml
send_time_range:
name: “Send Time Range (Experimental)”
description: “EXPERIMENTELL: Zeigt einen dauerhaften farbigen Ring-Balken mit Text für eine Dauer. Zuordnung ‘area’ zu echten Uhrzeigerpositionen ist noch ungeklärt.”
fields:
text:
name: “Text”
selector:
text:
color:
name: “Farbe”
default: “blue”
selector:
select:
options: [“white”, “red”, “blue”, “lime”, “dark_orange”, “blue_violet”, “lawn_green”]
duration_seconds:
name: “Dauer (Sekunden)”
required: true
selector:
number:
min: 5
max: 86400
area:
name: “Ring-Segment (experimentell, Mapping unbekannt)”
default: 0
selector:
number:
min: 0
max: 100
scene_slot:
name: “Szenen-Slot”
default: 9
selector:
number:
min: 0
max: 15
7.2 Test-Protokoll & Beobachtungen (bisher problematisch)
Test Parameter Beobachtung
1 color: blue, duration: 30s, area: 0, scene_slot: 9 Blauer Balken erscheint am Ring, ca. Position “2–3 Uhr”. Farbe/Sichtbarkeit funktionieren grundsätzlich.
2–n Verschiedene area, color (rot, grün, blau), verschiedene duration_seconds Keine sichtbare Änderung — immer derselbe blaue Fleck an derselben Stelle (2–3 Uhr)
Test nach 12h — Blauer Fleck war immer noch da, sporadisch wiederkehrend — lifeTime hat NICHT wie erwartet zur automatischen Löschung geführt
Stromreset der Uhr Uhr kurz vom Strom getrennt Blauer Fleck war danach weg
BT-Interferenz gefunden Log zeigt 16:17–17:07 Uhr mehrfache Verbindungsabbrüche (“Interference/range”) Erklärt evtl., warum spätere Testaufrufe nicht ankamen — Verbindung war zeitweise tot
Test nach BT-Stabilisierung text: “Test3”, color: green, duration: 10s, area: 24, scene_slot: 7, Uhr laut UI “Verbunden” Ergebnis identisch zu Test 1 — derselbe blaue Fleck an derselben Stelle, ignoriert color: green und area: 24 komplett. Kommt “immer wieder”.

Zwischenfazit / Verdachtsmomente (noch ungeklärt):

Möglich, dass der color/area-Parameter aus nachfolgenden Aufrufen aus irgendeinem Grund nicht im tatsächlich gesendeten Byte-Kommando ankommt (z. B. Fehler in der Service-Datenübergabe, Standardwerte greifen fälschlich).
Möglich, dass scene_slot unterschiedlich ist (7 statt 9), aber die Uhr trotzdem nur eine alte, hängende Szene auf Slot 9 zyklisch mitanzeigt, weil diese nie sauber gelöscht wurde (fehlende Lösch-Funktion — es gibt noch keinen ScenesDelete/ScenesClear-Aufruf im Code!).
Möglich, dass das Byte-Encoding des Headers ([0, 0, 24, scene_slot]) oder die Protobuf-Serialisierung strukturell nicht korrekt ist und die Uhr defaultet auf eine Fallback-Anzeige.
Noch nicht verifiziert: Kommt der Log-Eintrag “Sending time range scene…” / “…sent successfully” bei Test 3 tatsächlich im Log an? (Letzter Diagnoseschritt vor dieser Aktualisierung noch nicht abgeschlossen.)

Nächste Diagnoseschritte (in Arbeit, noch nicht abgeschlossen):

Live-Log direkt nach einem frischen Testaufruf prüfen → Erledigt, siehe 7.3
Fehlende ScenesDelete/Cleanup-Funktion nachrüsten, um hängende Szenen sauber zu entfernen (aktuell einziger zuverlässiger Weg: Stromreset der Uhr)
Segment-Mapping (area → echte Ziffernblatt-Position) ist weiterhin komplett ungeklärt
7.3 Diagnose-Ergebnis (10.08., 23:17 Uhr): Code funktioniert, aber area wirkt nicht

Log-Level-Anpassung vorgenommen (falls noch nicht in configuration.yaml):

yaml
logger:
default: warning
logs:
custom_components.glance_clock: debug

Bestätigter Live-Test (text: “Diagnose2”, color: red, duration_seconds: 15, area: 40, scene_slot: 6):

2026-08-10 23:17:58.003 INFO [custom_components.glance_clock.notify] Sending time range scene: text=Diagnose2, color=5, duration=15s, area=40, slot=6
2026-08-10 23:17:58.357 INFO [custom_components.glance_clock.notify] Time range scene sent successfully
2026-08-10 23:17:58.357 INFO [custom_components.glance_clock.services.time_range] Time range scene sent successfully: Diagnose2

:right_arrow: Parameterübergabe und BLE-Versand funktionieren zu 100% korrekt. Das Problem liegt zwischen “Kommando kommt bei der Uhr an” und “Uhr rendert es korrekt”.

Ergebnis auf der Uhr: Farbe wechselte korrekt von Blau → Rot :white_check_mark:. Position blieb exakt gleich trotz area 0 → 24 → 40 :cross_mark:.

Schlussfolgerung: MovingBarData.area steuert vermutlich nicht die Startposition auf dem Ring (evtl. Balkenbreite, Animationsbereich, oder wird nur bei speed > 0 überhaupt ausgewertet). Für “zünde exakt Segmente X bis Y” ist vermutlich CustomScene.Object.FillData.segment (Methode Fill, Wert 2 statt MovingBar=5) das richtige Feld — eine Liste konkreter LED-Segment-Indizes statt eines animierten Balkens mit unklarem Startpunkt.

Nächster Schritt (geplant, noch nicht umgesetzt): Neue Testfunktion mit method: Fill (2) und FillData.segment = [Liste von Ints], um herauszufinden, wie viele Segmente ein Vollkreis hat und wo Segment 0 auf dem Zifferblatt sitzt.

7.4 Test mit method: Fill — Ergebnis: Kein sichtbarer Effekt (11.08., 11:31–11:33 Uhr)

Neue Funktion async_send_fill_test in notify.py ergänzt (method=2/Fill statt method=5/MovingBar), Service glance_clock.send_fill_test registriert (analog zu send_time_range, in services/init.py Import/Wrapper/Registrierung ergänzt — bestätigt korrekt eingebaut).

Testreihe:

11:31:56 segments=[0], slot=6 → Command sent successfully (with response)
11:32:53 segments=[6], slot=6 → Command sent successfully (with response)
11:33:01 segments=[12], slot=6 → Command sent successfully (with response)
11:33:10 segments=[24], slot=6 → Command sent successfully (with response)

Alle vier BLE-Kommandos wurden von der Uhr auf Protokollebene bestätigt (“with response” = ACK erhalten). Aber: Die Uhr zeigte bei keinem einzigen Test irgendeine sichtbare Veränderung — nicht mal an der alten (blauen/roten) Position von den MovingBar-Tests. Kompletter visueller Nulleffekt.

Schlussfolgerung: method: Fill (allein, ohne weitere Felder) scheint von der Firmware entweder ignoriert oder benötigt zusätzliche Pflichtfelder, die uns unbekannt sind (evtl. muss die Szene separat “gestartet” werden, oder Fill ist nur in Kombination mit anderen Object-Feldern wirksam, oder das genutzte display_mode (24 = “Ring & Text”) passt nicht zu reinen Fill-Only-Szenen ohne Text).

Bilanz nach diesem Entwicklungsschritt:

:white_check_mark: Farbige Anzeige grundsätzlich möglich (MovingBar, Farbe funktioniert zuverlässig)
:cross_mark: Position/Segment-Steuerung bisher nicht reproduzierbar herstellbar (area bei MovingBar wirkungslos, Fill komplett wirkungslos)
:cross_mark: Automatisches Löschen der Szene nach lifeTime funktionierte in einem Test nicht zuverlässig (Szene blieb ~12h aktiv, nur Stromreset half)
Aufwand/Ergebnis: Deutlich umfangreicheres Reverse-Engineering nötig als ursprünglich angenommen, ohne Erfolgsgarantie — dies ist unerforschtes Terrain jenseits der offiziellen Doku/Community-Wissen zu diesem Gerät.

Offene Entscheidung: Weiter experimentieren (z. B. method: AreaAnimation, oder Kombinationen aus fill + movingBar + text im selben Object, oder rohe Byte-Analyse mit BLE-Sniffer) — oder vorerst pausieren und auf den einfacheren send_notice-Kompromiss (Start/Ende-Blitz) zurückfallen, der zuverlässig funktioniert.

7.5 Test mit ScenesStart (Kommando 31) nach Fill — Ergebnis: Ebenfalls kein Effekt (11.08.)

Aus der vollständigen Readme-Tabelle von GitHub - Hypfer/glance-clock: Glance Clock BLE protocol reverse-engineered · GitHub ergab sich der Verdacht, dass gespeicherte Szenen erst per ScenesStart (Kommando 31) aktiv “durchgeblättert” werden müssen, da diese laut Doku nur “Go to next scene slot” bewirkt und Szenen sonst nur im Speicher liegen.

Neue Methode async_scenes_start in notify.py ergänzt, Service glance_clock.scenes_start registriert (analog zu den anderen, in services/init.py korrekt eingebaut nach kurzer Nachbesserung des fehlenden Wrapper-/Registrierungs-Blocks).

Test:

yaml
action: glance_clock.send_fill_test
data: {segments: [0], scene_slot: 6}

action: glance_clock.scenes_start
data: {}

Ergebnis: Kein sichtbarer Effekt auf der Uhr — weder durch Fill allein, noch durch das nachgeschobene ScenesStart.

Fazit nach diesem Entwicklungsschritt:

MovingBar (method=5) ist bisher die einzige Methode, die überhaupt eine sichtbare Reaktion auslöst (Farbe ändert sich zuverlässig), aber Positionskontrolle (area) bleibt wirkungslos
Fill (method=2) zeigt nie einen sichtbaren Effekt, auch nicht in Kombination mit ScenesStart
Wir haben jetzt mehrere plausible Hypothesen durchgetestet (Positions-Parameter, fehlendes Aktivierungskommando) ohne Erfolg
Realistischer nächster Schritt wäre BLE-Traffic-Sniffing der offiziellen Glance-Clock-App (Android/iOS) beim Senden einer echten Szene, um das korrekte Byte-Format 1:1 abzugleichen — das erfordert zusätzliche Hardware/Software (BLE-Sniffer, gerootetes Testgerät oder HCI-Snoop-Log) und geht über das hinaus, was sich per Chat-gestützter Fehlersuche sinnvoll weiterverfolgen lässt

Empfehlung: Diesen experimentellen Pfad an dieser Stelle pausieren. Der zuverlässig funktionierende send_notice-Ansatz (kurzer Farbblitz bei Start/Ende eines Zeitraums) bleibt die praktikable Lösung für den produktiven Einsatz. Der bisherige Code (send_time_range, send_fill_test, scenes_start) bleibt als Grundlage im System erhalten, falls später mit BLE-Sniffing-Daten weitergemacht werden soll.

Update: Nutzer möchte doch noch einen letzten Ansatz testen (AreaAnimation/LightFlash, hat einen eigenen color-Parameter), siehe 7.6.

7.6 Neuer Versuch: AreaAnimation mit LightFlash (11.08., in Vorbereitung)

Begründung: AreaAnimation (method=7) mit type: LightFlash hat im Protobuf-Schema ein eigenes color-Feld direkt im LightFlashData-Objekt, kombiniert mit einer area-Liste (Segmente). Das könnte Position UND Farbe zuverlässiger verknüpfen als die bisherigen Versuche.

Neue Methode async_send_area_animation in notify.py ergänzt:

python
async def async_send_area_animation(self, segments, color, speed=0, duration_seconds=15, scene_slot=6) → bool:
“”“EXPERIMENTAL: Test AreaAnimation with LightFlash (has its own color field).”“”
if not self._connection_manager or not self._connection_manager.is_connected:
_LOGGER.warning(“Device not connected, cannot send area animation”)
return False
try:
from .glance_pb2 import CustomScene # type: ignore
scene = CustomScene()
obj = scene.object.add()
obj.method = 7 # AreaAnimation
obj.lifeTime = int(duration_seconds)
obj.areaAnimation.type = 2 # LightFlash
for seg in segments:
obj.areaAnimation.area.append(int(seg))
obj.areaAnimation.flashLight.color = int(color)
obj.areaAnimation.flashLight.speed = int(speed)
scene_bytes = scene.SerializeToString()
header = bytearray([0, 0, 24, scene_slot])
command = header + scene_bytes
_LOGGER.info(f"Sending area animation: segments={segments}, color={color}, speed={speed}, duration={duration_seconds}s, slot={scene_slot}“)
success = await self._connection_manager.send_command(bytes(command))
return success
except Exception as e:
_LOGGER.error(f"Error sending area animation: {e}”)
return False

Zugehöriger Service glance_clock.send_area_animation in services/time_range.py und services/init.py registriert (analog zu den vorherigen, Import/Wrapper/Registrierung ergänzt).

Status: Code steht, Test noch nicht durchgeführt (siehe 7.7 für wichtige Zwischenkorrektur, die vor dem Test nötig war).

7.7 Wichtiger Bugfix in notify.py (11.08.)

Beim Einbau der neuen Funktionen hat der Nutzer eigenständig notify.py aufgeräumt und dabei zwei echte Probleme gefunden und behoben:

Fehlender return im Exception-Handler von async_read_current_settings: Der ursprüngliche Code konnte in bestimmten Fehlerfällen zu einem NameError führen, da raw_data nicht in jedem Pfad initialisiert war. Fix: raw_data = b"" vor dem try-Block gesetzt und return None im except-Zweig ergänzt.
Doppelte/falsch platzierte Klassen-Docstring entfernt, die mitten im Dateikörper zwischen Methoden stand (Überbleibsel einer früheren Bearbeitung).

Kritischer Fund bei der Überprüfung: Die beiden Modul-Funktionen async_setup_entry und async_unload_entry (außerhalb der Klasse, am Dateiende) — die zwingend nötig sind, damit HA die notify-Plattform überhaupt lädt (das war exakt das Problem ganz zu Beginn dieser Session: notify.glance_clock not found) — fehlten in der vom Nutzer geposteten Version. Es war unklar, ob sie in der echten Datei nur beim Copy-Paste abgeschnitten wurden oder tatsächlich fehlten.

Maßnahme: Vollständige, korrigierte notify.py wurde als Datei bereitgestellt (inkl. Bugfix, allen experimentellen Methoden async_send_time_range, async_send_fill_test, async_scenes_start, async_send_area_animation, UND den beiden essenziellen Modul-Funktionen am Ende). Diese komplette Datei sollte die aktuell gültige /config/custom_components/glance_clock/notify.py ersetzen.

Noch zu tun:

Bestätigen, dass die bereitgestellte komplette Datei übernommen wurde
Nach Neustart: AreaAnimation-Test durchführen (siehe 7.6 für Service-Aufruf-Beispiel unten)
Test-Aufruf:
yaml
action: glance_clock.send_area_animation
data:
segments: [0]
color: red
speed: 0
duration_seconds: 15
scene_slot: 6

Falls speed: 0 nichts zeigt, zusätzlich mit speed: 500 testen.

Offene To-Dos (Zusammenfassung)
In Arbeit: Custom-Scene-Feature (send_time_range) — Grundgerüst steht, aber color/area-Parameter scheinen nicht anzukommen bzw. immer dieselbe (evtl. alte/hängende) Szene wird angezeigt. Debugging läuft (siehe Abschnitt 7.2).
Sound-Schalter (input_boolean.glance_clock_sound_aktiv) final in Packages-Datei und Automation 2 übernehmen (Code oben vorbereitet, Übernahmestatus unklar)
Optional: Zeit-Picker-Card (HACS) installieren und Popup-Content im Template darauf umstellen (Code oben vorbereitet, Fix für Template-Syntax nötig)
Ggf. Schalter für Sound irgendwo im Dashboard sichtbar platzieren
Fehlende Scene-Cleanup-Funktion (ScenesDelete) für send_time_range ergänzen, um hängende Szenen ohne Stromreset entfernen zu können
Ursache für “Parameter werden ignoriert” bei send_time_range finden (siehe Verdachtsmomente 7.2)

1 „Gefällt mir“