Correct alarm data findings from byte tests

The alarm station byte is shared and follows the current radio station, so
remove the per-alarm control. Remove the FM sound option, as writing it just
turns on the radio mode. Record how the MCU treats each DP127 byte.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
mangavalkandClaude Opus 5.5 committed 2026-11-05 17:38:23 +01:00
1 parent aa929478f6
commit e6f004e811
5 files changed
+25 -35

No files matched your search

+7 -1
View File
@@ -56,7 +56,13 @@ From Tuya's definition for a sibling product (tuya-local issue #3821). See the R
- A knock during a ring snoozes it. DP130 then stays ON. Outside an alarm it returns to OFF at once. - A knock during a ring snoozes it. DP130 then stays ON. Outside an alarm it returns to OFF at once.
- DP127 writes are ignored while an alarm is snoozed. - DP127 writes are ignored while an alarm is snoozed.
- A mode without light sets brightness to 0, and a light mode with brightness 0 falls back to no light, so the firmware sets a level when choosing a light mode. - A mode without light sets brightness to 0, and a light mode with brightness 0 falls back to no light, so the firmware sets a level when choosing a light mode.
- Alarm radio stations were reset to 1 while no stations were saved. - Byte tests on DP127 with Alarm 4 off: time, days, ring duration, volume, sunrise and (in light modes) brightness are stored as written, with no range check, so the controls must limit them. The time's high byte keeps only its low 4 bits.
- Byte 5 is bits: 0 = light, 1 = radio. Above 3 is ignored.
- Byte 6: writing 8 or more turns on the radio bit and sets the sound to 1. It's locked at 1 in radio modes.
- Byte 8 isn't per alarm. Every DP127 write sets all 4 to the current radio station, so there's no per-alarm station control.
- Byte 10 is forced to 0 without the light bit, and writing 0 clears the light bit.
- Byte 11: above 1 becomes 1.
- What byte 4 actually does to ring length isn't confirmed. A test ring wasn't observed stopping.
- Radio Volume and station writes are ignored with the radio off. With it on, both work, and the station stays selected after turning it off. - Radio Volume and station writes are ignored with the radio off. With it on, both work, and the station stays selected after turning it off.
- Keep `default_transition_length` at 0. A transition sends about 50 MCU writes a second. - Keep `default_transition_length` at 0. A transition sends about 50 MCU writes a second.
- LibreTiny before v1.14.0 can hang after a software restart or OTA until powered off for about 30 seconds (LibreTiny issue #396). The config pins v1.14.0. Drop the pin once ESPHome defaults to it or newer. - LibreTiny before v1.14.0 can hang after a software restart or OTA until powered off for about 30 seconds (LibreTiny issue #396). The config pins v1.14.0. Drop the pin once ESPHome defaults to it or newer.
+2 -2
View File
@@ -140,13 +140,13 @@ Copy `server-example.yaml` to the server and add `wifi_ssid`, `wifi_password`, `
Tuya's data model for this family was posted in [tuya-local issue #3821](https://github.com/make-all/tuya-local/issues/3821) for the sibling product `rslo8vqc1xjtaoy9`. It matches what this unit reports, and it replaces the older notes below where they differ. Tuya's data model for this family was posted in [tuya-local issue #3821](https://github.com/make-all/tuya-local/issues/3821) for the sibling product `rslo8vqc1xjtaoy9`. It matches what this unit reports, and it replaces the older notes below where they differ.
- **DP127** is 48 bytes on the unit tested here: 12 bytes per alarm, 4 alarms. Logs cut it off at 16 bytes, which likely explains the older 16-byte notes. Bytes: 0 enabled; 1-2 minutes since midnight; 3 repeat days (bit 0 = Monday, 0 = one time); 4 ring duration; 5 mode (0 sound, 1 sound + light, 2 radio, 3 radio + light); 6 sound; 7 volume; 8 radio station; 9 sunrise minutes; 10 brightness after sunrise; 11 snooze (0 allowed, 1 not). - **DP127** is 48 bytes on the unit tested here: 12 bytes per alarm, 4 alarms. Logs cut it off at 16 bytes, which likely explains the older 16-byte notes. Bytes: 0 enabled; 1-2 minutes since midnight; 3 repeat days (bit 0 = Monday, 0 = one time); 4 ring duration; 5 mode (bit 0 light, bit 1 radio); 6 sound 0-7; 7 volume; 8 current radio station, shared by all alarms; 9 sunrise minutes; 10 brightness after sunrise; 11 snooze (0 allowed, 1 not). The older "8 = FM" sound is really the MCU turning on the radio bit.
- **DP128** is 10 bytes: 0 unused; 1 mode, as above; 2 sound 1-4; 3 volume; 4 sound duration; 5 radio station; 6 radio duration; 7 start brightness; 8 sunset duration; 9 snooze. Bytes 4, 6 and 8 should match. - **DP128** is 10 bytes: 0 unused; 1 mode, as above; 2 sound 1-4; 3 volume; 4 sound duration; 5 radio station; 6 radio duration; 7 start brightness; 8 sunset duration; 9 snooze. Bytes 4, 6 and 8 should match.
- **DP107** is stations found by a scan, then the current station. **DP126** starts (1) or ends (0) a channel search. - **DP107** is stations found by a scan, then the current station. **DP126** starts (1) or ends (0) a channel search.
- **DP117** and **DP132** are enums on this unit, so values count from 0. DP132 runs cycle, red, orange, yellow, green, cyan, blue, purple. - **DP117** and **DP132** are enums on this unit, so values count from 0. DP132 runs cycle, red, orange, yellow, green, cyan, blue, purple.
- **DP125** is online time synchronization. **DP130** is snooze. - **DP125** is online time synchronization. **DP130** is snooze.
Observed behaviour on this unit: a mode without light sets brightness to 0, and a light mode with brightness 0 falls back to no light. Alarm stations were reset to 1 while no stations had been scanned. DP127 writes are ignored while an alarm is snoozed. DP130 stays on after snoozing a ringing alarm and returns to off at once otherwise. Default light transitions flood the MCU with writes, so keep them at 0. Observed behaviour on this unit: a mode without light sets brightness to 0, and a light mode with brightness 0 falls back to no light. Every DP127 write sets byte 8 of all four alarms to the current radio station. Most other bytes are stored without any range check. DP127 writes are ignored while an alarm is snoozed. DP130 stays on after snoozing a ringing alarm and returns to off at once otherwise. Default light transitions flood the MCU with writes, so keep them at 0.
## DP127 alarm structure ## DP127 alarm structure
+2 -22
View File
@@ -124,7 +124,6 @@ select:
- "Sound 5" - "Sound 5"
- "Sound 6" - "Sound 6"
- "Sound 7" - "Sound 7"
- "FM Radio"
update_interval: 2s update_interval: 2s
web_server: web_server:
sorting_group_id: group_alarm_${n} sorting_group_id: group_alarm_${n}
@@ -133,7 +132,6 @@ select:
if (!id(alarm_raw_valid)) return {}; if (!id(alarm_raw_valid)) return {};
uint8_t s = id(alarm_raw)[${o} + 6]; uint8_t s = id(alarm_raw)[${o} + 6];
if (s == 0) return std::string("Off"); if (s == 0) return std::string("Off");
if (s == 8) return std::string("FM Radio");
if (s >= 1 && s <= 7) return std::string("Sound ") + std::to_string(s); if (s >= 1 && s <= 7) return std::string("Sound ") + std::to_string(s);
return {}; return {};
set_action: set_action:
@@ -147,7 +145,6 @@ select:
else if (x == "Sound 5") value = 5; else if (x == "Sound 5") value = 5;
else if (x == "Sound 6") value = 6; else if (x == "Sound 6") value = 6;
else if (x == "Sound 7") value = 7; else if (x == "Sound 7") value = 7;
else if (x == "FM Radio") value = 8;
id(alarm_raw)[${o} + 6] = value; id(alarm_raw)[${o} + 6] = value;
- script.execute: send_alarm_settings - script.execute: send_alarm_settings
@@ -174,7 +171,8 @@ number:
- platform: template - platform: template
id: alarm_${n}_wake_brightness id: alarm_${n}_wake_brightness
name: "Alarm ${n} Wake Light" name: "Alarm ${n} Wake Light"
min_value: 1 # Reads 0 in modes without light, where the MCU ignores changes.
min_value: 0
max_value: 20 max_value: 20
step: 1 step: 1
update_interval: 2s update_interval: 2s
@@ -211,24 +209,6 @@ number:
id(alarm_raw)[${o} + 4] = static_cast<uint8_t>(x); id(alarm_raw)[${o} + 4] = static_cast<uint8_t>(x);
- script.execute: send_alarm_settings - script.execute: send_alarm_settings
- platform: template
id: alarm_${n}_radio_station
name: "Alarm ${n} Radio Station"
min_value: 1
max_value: 60
step: 1
update_interval: 2s
web_server:
sorting_group_id: group_alarm_${n}
sorting_weight: 9
lambda: |-
if (!id(alarm_raw_valid)) return {};
return static_cast<float>(id(alarm_raw)[${o} + 8]);
set_action:
- lambda: |-
if (!id(alarm_raw_valid)) return;
id(alarm_raw)[${o} + 8] = static_cast<uint8_t>(x);
- script.execute: send_alarm_settings
- platform: template - platform: template
id: alarm_${n}_sunrise_duration id: alarm_${n}_sunrise_duration
+1 -1
View File
@@ -7,6 +7,6 @@ substitutions:
packages: packages:
lamp: lamp:
url: https://mangavalk.com/mangavalk/esphome-wake-up-light-ii.git url: https://mangavalk.com/mangavalk/esphome-wake-up-light-ii.git
ref: v1.0.1 ref: v1.0.2
files: [wake-up-light-ii.yaml] files: [wake-up-light-ii.yaml]
refresh: 1d refresh: 1d
+13 -9
View File
@@ -22,7 +22,7 @@ external_components:
- source: - source:
type: git type: git
url: https://mangavalk.com/mangavalk/esphome-wake-up-light-ii.git url: https://mangavalk.com/mangavalk/esphome-wake-up-light-ii.git
ref: v1.0.1 ref: v1.0.2
components: [uart] components: [uart]
refresh: 1d refresh: 1d
@@ -266,14 +266,18 @@ tuya:
# [00] enabled # [00] enabled
# [01:02] time = minutes since midnight, big-endian # [01:02] time = minutes since midnight, big-endian
# [03] repeat days: bit 0 = Monday ... bit 6 = Sunday, 0 = one time # [03] repeat days: bit 0 = Monday ... bit 6 = Sunday, 0 = one time
# [04] ring duration (Tuya says 10-20, this lamp reports 30) # [04] ring duration (Tuya says 10-20, this lamp reports 30; any value is kept)
# [05] mode: 0=sound, 1=sound+light, 2=radio, 3=radio+light # [05] mode bits: bit 0=light, bit 1=radio. Values above 3 are ignored.
# [06] sound (this project also observed 8=FM) # [06] sound 0..7. Writing 8 or more turns on the radio bit and resets it to 1.
# [07] sound volume 1..16 # Locked at 1 in radio modes.
# [08] radio station 1..60 # [07] volume 1..16 (any value is kept)
# [09] sunrise lead time in minutes, 10..60 # [08] radio station. Not per alarm: every write sets all 4 to the current
# [10] brightness after sunrise, 1..20 # radio station, so don't try to set it here.
# [11] snooze: 0=allowed, 1=not allowed # [09] sunrise lead time in minutes, 10..60 (any value is kept)
# [10] brightness after sunrise, 1..20. Forced to 0 without the light bit, and
# 0 clears the light bit.
# [11] snooze: 0=allowed, 1=not allowed. Values above 1 become 1.
# The MCU doesn't range-check most bytes, so the controls must.
- sensor_datapoint: 127 - sensor_datapoint: 127
datapoint_type: raw datapoint_type: raw
then: then: