Decode DP127 as 48 bytes (4 alarms), plus DP128, DP107 and DP126, from Tuya's data model. Add day switches, alarm and sunset controls, a knock sensor on PA18, display schedule, bedtime fade, radio timer and grouped web UI. Pin LibreTiny v1.14.0 for the OTA restart fix. Ship the patched UART as an external component so a server needs only a short YAML. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
261 lines
8.1 KiB
YAML
261 lines
8.1 KiB
YAML
###############################################################################
|
|
# One alarm's controls. Included once per alarm from wake-up-light-ii.yaml.
|
|
# n = alarm number
|
|
# o = offset of this alarm's 12-byte block inside DP127
|
|
# dp = this alarm's on/off datapoint
|
|
# Byte layout is documented next to the DP127 handler in the main file.
|
|
###############################################################################
|
|
|
|
# Bit 0 = Monday, per Tuya's definition and a ringing test. No days set = one time.
|
|
packages:
|
|
mon: !include { file: alarm_day.yaml, vars: { day: "Mon", bit: "0", w: "11" } }
|
|
tue: !include { file: alarm_day.yaml, vars: { day: "Tue", bit: "1", w: "12" } }
|
|
wed: !include { file: alarm_day.yaml, vars: { day: "Wed", bit: "2", w: "13" } }
|
|
thu: !include { file: alarm_day.yaml, vars: { day: "Thu", bit: "3", w: "14" } }
|
|
fri: !include { file: alarm_day.yaml, vars: { day: "Fri", bit: "4", w: "15" } }
|
|
sat: !include { file: alarm_day.yaml, vars: { day: "Sat", bit: "5", w: "16" } }
|
|
sun: !include { file: alarm_day.yaml, vars: { day: "Sun", bit: "6", w: "17" } }
|
|
|
|
web_server:
|
|
sorting_groups:
|
|
- id: group_alarm_${n}
|
|
name: "Alarm ${n}"
|
|
sorting_weight: 3${n}
|
|
|
|
switch:
|
|
- platform: tuya
|
|
id: alarm_${n}_enable
|
|
name: "Alarm ${n}"
|
|
switch_datapoint: ${dp}
|
|
web_server:
|
|
sorting_group_id: group_alarm_${n}
|
|
sorting_weight: 1
|
|
|
|
# Stored inverted: 0 = snooze allowed, 1 = not allowed.
|
|
- platform: template
|
|
id: alarm_${n}_snooze_allowed
|
|
name: "Alarm ${n} Snooze Allowed"
|
|
web_server:
|
|
sorting_group_id: group_alarm_${n}
|
|
sorting_weight: 10
|
|
lambda: |-
|
|
if (!id(alarm_raw_valid)) return {};
|
|
return id(alarm_raw)[${o} + 11] == 0;
|
|
turn_on_action:
|
|
- lambda: |-
|
|
if (!id(alarm_raw_valid)) return;
|
|
id(alarm_raw)[${o} + 11] = 0;
|
|
- script.execute: send_alarm_settings
|
|
turn_off_action:
|
|
- lambda: |-
|
|
if (!id(alarm_raw_valid)) return;
|
|
id(alarm_raw)[${o} + 11] = 1;
|
|
- script.execute: send_alarm_settings
|
|
|
|
datetime:
|
|
- platform: template
|
|
id: alarm_${n}_time
|
|
name: "Alarm ${n} Time"
|
|
type: time
|
|
update_interval: 2s
|
|
web_server:
|
|
sorting_group_id: group_alarm_${n}
|
|
sorting_weight: 2
|
|
lambda: |-
|
|
if (!id(alarm_raw_valid)) return {};
|
|
uint16_t mins = (static_cast<uint16_t>(id(alarm_raw)[${o} + 1]) << 8) | id(alarm_raw)[${o} + 2];
|
|
if (mins >= 1440) return {};
|
|
ESPTime t{};
|
|
t.hour = mins / 60;
|
|
t.minute = mins % 60;
|
|
t.second = 0;
|
|
return t;
|
|
set_action:
|
|
- lambda: |-
|
|
if (!id(alarm_raw_valid)) return;
|
|
uint16_t mins = static_cast<uint16_t>(x.hour) * 60 + x.minute;
|
|
id(alarm_raw)[${o} + 1] = (mins >> 8) & 0xFF;
|
|
id(alarm_raw)[${o} + 2] = mins & 0xFF;
|
|
- script.execute: send_alarm_settings
|
|
|
|
select:
|
|
- platform: template
|
|
id: alarm_${n}_mode
|
|
name: "Alarm ${n} Mode"
|
|
options:
|
|
- "Sound"
|
|
- "Sound + Light"
|
|
- "Radio"
|
|
- "Radio + Light"
|
|
update_interval: 2s
|
|
web_server:
|
|
sorting_group_id: group_alarm_${n}
|
|
sorting_weight: 2.5
|
|
lambda: |-
|
|
if (!id(alarm_raw_valid)) return {};
|
|
switch (id(alarm_raw)[${o} + 5]) {
|
|
case 0: return std::string("Sound");
|
|
case 1: return std::string("Sound + Light");
|
|
case 2: return std::string("Radio");
|
|
case 3: return std::string("Radio + Light");
|
|
default: return {};
|
|
}
|
|
set_action:
|
|
- lambda: |-
|
|
if (!id(alarm_raw_valid)) return;
|
|
uint8_t value = 0;
|
|
if (x == "Sound + Light") value = 1;
|
|
else if (x == "Radio") value = 2;
|
|
else if (x == "Radio + Light") value = 3;
|
|
id(alarm_raw)[${o} + 5] = value;
|
|
// The MCU drops a light mode if brightness is 0, so give it a level.
|
|
if ((value == 1 || value == 3) && id(alarm_raw)[${o} + 10] == 0) id(alarm_raw)[${o} + 10] = 10;
|
|
- script.execute: send_alarm_settings
|
|
|
|
- platform: template
|
|
id: alarm_${n}_sound
|
|
name: "Alarm ${n} Sound"
|
|
options:
|
|
- "Off"
|
|
- "Sound 1"
|
|
- "Sound 2"
|
|
- "Sound 3"
|
|
- "Sound 4"
|
|
- "Sound 5"
|
|
- "Sound 6"
|
|
- "Sound 7"
|
|
- "FM Radio"
|
|
update_interval: 2s
|
|
web_server:
|
|
sorting_group_id: group_alarm_${n}
|
|
sorting_weight: 3
|
|
lambda: |-
|
|
if (!id(alarm_raw_valid)) return {};
|
|
uint8_t s = id(alarm_raw)[${o} + 6];
|
|
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);
|
|
return {};
|
|
set_action:
|
|
- lambda: |-
|
|
if (!id(alarm_raw_valid)) return;
|
|
uint8_t value = 0;
|
|
if (x == "Sound 1") value = 1;
|
|
else if (x == "Sound 2") value = 2;
|
|
else if (x == "Sound 3") value = 3;
|
|
else if (x == "Sound 4") value = 4;
|
|
else if (x == "Sound 5") value = 5;
|
|
else if (x == "Sound 6") value = 6;
|
|
else if (x == "Sound 7") value = 7;
|
|
else if (x == "FM Radio") value = 8;
|
|
id(alarm_raw)[${o} + 6] = value;
|
|
- script.execute: send_alarm_settings
|
|
|
|
number:
|
|
- platform: template
|
|
id: alarm_${n}_volume
|
|
name: "Alarm ${n} Volume"
|
|
min_value: 1
|
|
max_value: 16
|
|
step: 1
|
|
update_interval: 2s
|
|
web_server:
|
|
sorting_group_id: group_alarm_${n}
|
|
sorting_weight: 4
|
|
lambda: |-
|
|
if (!id(alarm_raw_valid)) return {};
|
|
return static_cast<float>(id(alarm_raw)[${o} + 7]);
|
|
set_action:
|
|
- lambda: |-
|
|
if (!id(alarm_raw_valid)) return;
|
|
id(alarm_raw)[${o} + 7] = static_cast<uint8_t>(x);
|
|
- script.execute: send_alarm_settings
|
|
|
|
- platform: template
|
|
id: alarm_${n}_wake_brightness
|
|
name: "Alarm ${n} Wake Light"
|
|
min_value: 1
|
|
max_value: 20
|
|
step: 1
|
|
update_interval: 2s
|
|
web_server:
|
|
sorting_group_id: group_alarm_${n}
|
|
sorting_weight: 5
|
|
lambda: |-
|
|
if (!id(alarm_raw_valid)) return {};
|
|
return static_cast<float>(id(alarm_raw)[${o} + 10]);
|
|
set_action:
|
|
- lambda: |-
|
|
if (!id(alarm_raw_valid)) return;
|
|
id(alarm_raw)[${o} + 10] = static_cast<uint8_t>(x);
|
|
- script.execute: send_alarm_settings
|
|
|
|
# Tuya's definition says 10-20, default 15, but this lamp reports 30.
|
|
- platform: template
|
|
id: alarm_${n}_ring_duration
|
|
name: "Alarm ${n} Ring Duration"
|
|
unit_of_measurement: "min"
|
|
min_value: 10
|
|
max_value: 60
|
|
step: 5
|
|
update_interval: 2s
|
|
web_server:
|
|
sorting_group_id: group_alarm_${n}
|
|
sorting_weight: 8
|
|
lambda: |-
|
|
if (!id(alarm_raw_valid)) return {};
|
|
return static_cast<float>(id(alarm_raw)[${o} + 4]);
|
|
set_action:
|
|
- lambda: |-
|
|
if (!id(alarm_raw_valid)) return;
|
|
id(alarm_raw)[${o} + 4] = static_cast<uint8_t>(x);
|
|
- 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
|
|
id: alarm_${n}_sunrise_duration
|
|
name: "Alarm ${n} Sunrise Duration"
|
|
unit_of_measurement: "min"
|
|
min_value: 10
|
|
max_value: 60
|
|
step: 10
|
|
update_interval: 2s
|
|
web_server:
|
|
sorting_group_id: group_alarm_${n}
|
|
sorting_weight: 6
|
|
lambda: |-
|
|
if (!id(alarm_raw_valid)) return {};
|
|
return static_cast<float>(id(alarm_raw)[${o} + 9]);
|
|
set_action:
|
|
- lambda: |-
|
|
if (!id(alarm_raw_valid)) return;
|
|
id(alarm_raw)[${o} + 9] = static_cast<uint8_t>(x);
|
|
- script.execute: send_alarm_settings
|
|
|
|
text_sensor:
|
|
- platform: template
|
|
id: alarm_${n}_summary
|
|
name: "Alarm ${n} Summary"
|
|
update_interval: never
|
|
web_server:
|
|
sorting_group_id: group_alarm_${n}
|
|
sorting_weight: 7
|