diff --git a/lampOS/Cargo.lock b/lampOS/Cargo.lock index 106724460f..d045035ce1 100644 --- a/lampOS/Cargo.lock +++ b/lampOS/Cargo.lock @@ -2219,8 +2219,12 @@ version = "0.1.0" dependencies = [ "base64 0.22.1", "httparse", + "lamp-actions", "lamp-bus", + "lamp-gemini", "lamp-msgs", + "lamp-skills", + "lamp-voice", "ring", "rustix", "rustls", diff --git a/lampOS/crates/update/Cargo.toml b/lampOS/crates/update/Cargo.toml index 9492f0a656..cc9a2acfe1 100644 --- a/lampOS/crates/update/Cargo.toml +++ b/lampOS/crates/update/Cargo.toml @@ -35,6 +35,12 @@ rustix = { version = "=1.1.5", features = ["fs"] } [dev-dependencies] tempfile = "3" +# The functions a product lamp declares to a session, worked out by the +# voice, the actions part and the skills' own code (tests/functions.rs). +lamp-voice = { path = "../voice" } +lamp-actions = { path = "../actions" } +lamp-skills = { path = "../skills" } +lamp-gemini = { path = "../gemini" } [lints] workspace = true diff --git a/lampOS/crates/update/src/install.rs b/lampOS/crates/update/src/install.rs index 0fb360600a..21990bfd41 100644 --- a/lampOS/crates/update/src/install.rs +++ b/lampOS/crates/update/src/install.rs @@ -155,8 +155,52 @@ pub const PRODUCT: &[(&str, &str)] = &[ ), ]; +/// The skills every lamp runs: each a folder of `lampOS/skills/`, put in +/// `/var/lib/lampOS/skills/` whole but for its `tests/` ([`SETTINGS`]). +/// Every behaviour that has a skill runs as the skill (the owner, 19 Oct): +/// the voice's own hello is off in `lamp-voice.service`, and its reminders +/// and task news wait for the skill's ask (`LAMP_SCHEDULE_SPEAK=skill`, +/// `LAMP_TASK_NEWS=skill`). +pub const PRODUCT_SKILLS: &[&str] = &[ + "welcome-back", + "reminders", + "task-news", + "stretch-break", + "stuffy-air", + "dance", +]; + +/// The skills of `lampOS/skills/` a lamp does not get yet, each with why. +/// A product session's functions are 31 of the 32 it takes +/// (`tests/functions.rs`): the two tools of Desk tutor and its marks would +/// put the last of them past it, left out without a word. +pub const NOT_PRODUCT_SKILLS: &[(&str, &str)] = &[ + ( + "desk-tutor", + "it reads handwriting from the full look (`LAMP_LOOK_FULL=on` with `lamp-eyes --full-look`), \ + which the product doesn't run: its look is the working frame, 320 across; and with its \ + marks its two functions would be 33 of a session's 32", + ), + ( + "desk-tutor-marks", + "it is called only after Desk tutor's look, and Desk tutor isn't in the product", + ), +]; + +/// One file of a product skill, `lampOS/skills/SKILL/FILE`, and where +/// `lamp-skills` reads it on a lamp: `/var/lib/lampOS/skills/SKILL/FILE`. +macro_rules! skill_file { + ($skill:literal, $file:literal) => { + ( + concat!("/var/lib/lampOS/skills/", $skill, "/", $file), + include_str!(concat!("../../../skills/", $skill, "/", $file)), + ) + }; +} + /// The product's settings that no build carries, and where each goes: -/// in `/etc/lampOS//`, readable by the parts' user (docs/install.md). +/// in `/etc/lampOS//`, readable by the parts' user (docs/install.md), +/// and the product skills' files. pub const SETTINGS: &[(&str, &str)] = &[ ( "/etc/lampOS/expression/expression.json", @@ -177,20 +221,22 @@ pub const SETTINGS: &[(&str, &str)] = &[ "/etc/lampOS/records.json", include_str!("../../../deploy/product/settings/records.json"), ), - // The first skill (#811): Welcome back, the hello on arrival. Its tests - // stay in the repository; `lamp-skills` reloads the folder by itself. - ( - "/var/lib/lampOS/skills/welcome-back/skill.toml", - include_str!("../../../skills/welcome-back/skill.toml"), - ), - ( - "/var/lib/lampOS/skills/welcome-back/SKILL.md", - include_str!("../../../skills/welcome-back/SKILL.md"), - ), - ( - "/var/lib/lampOS/skills/welcome-back/lights/warm_glow.toml", - include_str!("../../../skills/welcome-back/lights/warm_glow.toml"), - ), + // The product skills ([`PRODUCT_SKILLS`]), every file but their tests, + // which stay in the repository; `lamp-skills` reloads the folder by + // itself. Welcome back, the hello on arrival, came first (#811). + skill_file!("welcome-back", "skill.toml"), + skill_file!("welcome-back", "SKILL.md"), + skill_file!("welcome-back", "lights/warm_glow.toml"), + skill_file!("reminders", "skill.toml"), + skill_file!("reminders", "SKILL.md"), + skill_file!("task-news", "skill.toml"), + skill_file!("task-news", "SKILL.md"), + skill_file!("stretch-break", "skill.toml"), + skill_file!("stretch-break", "SKILL.md"), + skill_file!("stuffy-air", "skill.toml"), + skill_file!("stuffy-air", "SKILL.md"), + skill_file!("dance", "skill.toml"), + skill_file!("dance", "SKILL.md"), ]; /// Setup's two settings (docs/setup.md, "On a lamp"), written only with diff --git a/lampOS/crates/update/tests/functions.rs b/lampOS/crates/update/tests/functions.rs new file mode 100644 index 0000000000..f67ecea725 --- /dev/null +++ b/lampOS/crates/update/tests/functions.rs @@ -0,0 +1,166 @@ +//! The functions a product lamp's voice tells each session of, with every +//! product skill installed. A session takes at most `lamp_gemini::MAX_TOOLS` +//! (32), and what is past it is left out by order, without a word: the +//! voice's own first, then the actions part's, then the skills' (the +//! coordinator, 19 Oct: lamp-4ace sat at exactly 32, and switching +//! onboarding on there pushed one of the actions part's out). So the +//! product's count is pinned here, worked out by each part's own code: the +//! voice's own from the product voice unit's environment, the actions +//! part's whole catalog (its sound, its light and Schedule's), and the tools +//! of `install::PRODUCT_SKILLS` as `lamp-skills` writes them. +use lamp_update::install; +use std::{ + path::{Path, PathBuf}, + process::Command, +}; + +const VOICE: &str = include_str!("../../../deploy/product/lamp-voice.service"); +/// Set only for the run in a process of its own: the folder of the +/// catalogs it reads, there as the product's `LAMP_ACTIONS_DIR`. +const CATALOGS: &str = "LAMP_TEST_PRODUCT_CATALOGS"; +/// The test run in a process of its own. +const VOICE_SIDE: &str = "the_product_voice_s_functions_in_a_process_of_their_own"; +/// How that process says each function given, and each left out: found +/// anywhere in a line, as the test runner may begin the first with the +/// test's name. +const DECLARED: &str = "declared: "; +const LEFT_OUT: &str = "left out: "; + +/// What a product lamp declares now: the voice's own 13, the actions part's +/// 17 and the skills' 1 (Dance's), one under the bound. A change to it is a +/// change to what every session is told: say in its pull request what is +/// left out past the bound, if anything. +const PRODUCT_FUNCTIONS: usize = 31; + +fn skills_root() -> PathBuf { + Path::new(env!("CARGO_MANIFEST_DIR")).join("../../skills") +} + +/// Every `Environment=` of the product voice unit's `[Service]`. +fn voice_environment() -> Vec<(String, String)> { + let mut inside = false; + let mut set = Vec::new(); + for line in VOICE.lines().map(str::trim) { + if line.starts_with('[') { + inside = line == "[Service]"; + } else if inside && let Some(pair) = line.strip_prefix("Environment=") { + let (name, value) = pair.split_once('=').unwrap(); + set.push((name.to_owned(), value.to_owned())); + } + } + set +} + +/// The voice's side, run in a process of its own by +/// [`a_product_lamp_declares_every_function_it_has_within_the_bound`] with +/// the product voice unit's environment and no other `LAMP_` setting, as +/// the voice's provider worker works it out: its flags from that +/// environment, its memory started (the rules' functions are told only +/// then), a session's functions, and what the link would leave out of +/// them. Does nothing in any other run. +#[test] +fn the_product_voice_s_functions_in_a_process_of_their_own() { + if std::env::var_os(CATALOGS).is_none() { + return; + } + let flags = lamp_voice::tools::arguments_from_environment(); + let (settings, rest) = lamp_voice::tools::take(&flags).unwrap(); + assert!(rest.is_empty(), "{rest:?}"); + if let Some(socket) = &settings.memory { + lamp_voice::remember::start(socket.clone(), None); + } + let (kept, left_out) = lamp_voice::tools::admitted(lamp_voice::tools::declared(&settings)); + for tool in kept { + println!("{DECLARED}{}", tool.name); + } + for why in left_out { + println!("{LEFT_OUT}{why}"); + } +} + +/// Every function the actions part and the product skills offer reaches a +/// product lamp's sessions, none left out by order or by the link, and the +/// count is [`PRODUCT_FUNCTIONS`], within `lamp_gemini::MAX_TOOLS`. +#[test] +fn a_product_lamp_declares_every_function_it_has_within_the_bound() { + let folder = tempfile::tempdir().unwrap(); + // The actions part's catalog with all it offers: sound and light, both + // there, and Schedule's (the product's LAMP_SCHEDULE_TOOLS=on). + let mut executive = lamp_actions::catalog(true, true); + executive.extend(lamp_actions::schedule::catalog()); + std::fs::write( + folder.path().join(lamp_voice::tools::CATALOG_FILE), + lamp_actions::catalog_file(&executive), + ) + .unwrap(); + // The skills' tools beside it, as `lamp-skills --tools-dir` writes them. + let skills: Vec = install::PRODUCT_SKILLS + .iter() + .map(|name| lamp_skills::load(&skills_root().join(name)).unwrap()) + .collect(); + let beside = folder.path().join(lamp_voice::tools::SKILLS_DIR); + std::fs::create_dir(&beside).unwrap(); + std::fs::write( + beside.join(lamp_voice::tools::CATALOG_FILE), + lamp_skills::tools_file(&skills).to_string(), + ) + .unwrap(); + + let mut command = Command::new(std::env::current_exe().unwrap()); + command.args([VOICE_SIDE, "--exact", "--nocapture", "--test-threads=1"]); + for (name, _) in std::env::vars_os() { + if name.to_string_lossy().starts_with("LAMP_") { + command.env_remove(name); + } + } + for (name, value) in voice_environment() { + if name == "LAMP_ACTIONS_DIR" { + command.env(name, folder.path()); + } else { + command.env(name, value); + } + } + let output = command.env(CATALOGS, folder.path()).output().unwrap(); + let said = String::from_utf8_lossy(&output.stdout); + assert!( + output.status.success(), + "{said}{}", + String::from_utf8_lossy(&output.stderr) + ); + let declared: Vec<&str> = said + .lines() + .filter_map(|line| line.split_once(DECLARED).map(|(_, name)| name)) + .collect(); + let left_out: Vec<&str> = said + .lines() + .filter_map(|line| line.split_once(LEFT_OUT).map(|(_, why)| why)) + .collect(); + assert!(left_out.is_empty(), "left out by the link: {left_out:?}"); + + let skill_tools: Vec<&str> = skills + .iter() + .filter_map(|skill| skill.tool.as_deref()) + .collect(); + for name in executive + .iter() + .map(|function| function.name) + .chain(skill_tools.iter().copied()) + { + assert!( + declared.contains(&name), + "{name} is left out of a product lamp's sessions, past {}: {declared:?}", + lamp_gemini::MAX_TOOLS + ); + } + println!( + "A product lamp declares {} functions of at most {}: the voice's own {}, the actions part's {}, the skills' {} ({})", + declared.len(), + lamp_gemini::MAX_TOOLS, + declared.len() - executive.len() - skill_tools.len(), + executive.len(), + skill_tools.len(), + skill_tools.join(", ") + ); + assert!(declared.len() <= lamp_gemini::MAX_TOOLS); + assert_eq!(declared.len(), PRODUCT_FUNCTIONS, "{declared:?}"); +} diff --git a/lampOS/crates/update/tests/install.rs b/lampOS/crates/update/tests/install.rs index 4cf7aff9c8..391667d976 100644 --- a/lampOS/crates/update/tests/install.rs +++ b/lampOS/crates/update/tests/install.rs @@ -866,21 +866,94 @@ fn unreadable_records_stop_install_rather_than_being_written_over() { lamp.install().unwrap(); } -/// #811, from lamp-06c1: a home lamp gets `lamp-skills` and its first skill, -/// Welcome back, and the voice speaks it through its say service with its -/// own hello off, so the lamp greets once. +/// The repository's skill folders, `lampOS/skills/`. +fn skills_root() -> PathBuf { + Path::new(env!("CARGO_MANIFEST_DIR")).join("../../skills") +} + +/// Every file of a skill folder but its tests, as paths in it. +fn skill_files(folder: &Path) -> Vec { + fn walk(folder: &Path, under: &str, files: &mut Vec) { + for entry in fs::read_dir(folder).unwrap().flatten() { + let name = entry.file_name().to_string_lossy().into_owned(); + let path = format!("{under}{name}"); + if entry.path().is_dir() { + if path != "tests" { + walk(&entry.path(), &format!("{path}/"), files); + } + } else { + files.push(path); + } + } + } + let mut files = Vec::new(); + walk(folder, "", &mut files); + files.sort(); + files +} + +/// The owner, 19 Oct: a lamp runs every behaviour that has a skill as the +/// skill. So every folder in `lampOS/skills/` is a product skill or says why +/// it isn't, and a new one is decided, not left out unseen. #[test] -fn a_blank_lamp_gets_lamp_skills_with_welcome_back_and_greets_once() { +fn every_skill_folder_is_in_the_product_or_says_why_not() { + let mut folders: Vec = fs::read_dir(skills_root()) + .unwrap() + .flatten() + .filter(|entry| entry.path().is_dir()) + .map(|entry| entry.file_name().to_string_lossy().into_owned()) + .filter(|name| !name.starts_with('.')) + .collect(); + folders.sort(); + let mut decided: Vec = install::PRODUCT_SKILLS + .iter() + .chain(install::NOT_PRODUCT_SKILLS.iter().map(|(name, _)| name)) + .map(|name| (*name).to_owned()) + .collect(); + decided.sort(); + assert_eq!(folders, decided); + for (name, why) in install::NOT_PRODUCT_SKILLS { + assert!(!why.is_empty(), "{name}"); + } +} + +/// #811, from lamp-06c1, and every skill since (the owner, 19 Oct): a home +/// lamp gets `lamp-skills` and every product skill whole but its tests, +/// byte for byte, and no other skill. The voice speaks them through its say +/// service; its own hello is off, so the lamp greets once, and its reminders +/// and task news wait for their skills' asks, so each is told once. +#[test] +fn a_blank_lamp_runs_every_product_skill_as_the_skill() { let mut lamp = Lamp::new(188, &[]); lamp.install().unwrap(); assert!(lamp.on("/opt/lampOS/skills/current/lamp-skills").exists()); - for file in ["skill.toml", "SKILL.md", "lights/warm_glow.toml"] { - let on_lamp = fs::read(lamp.on(&format!("/var/lib/lampOS/skills/welcome-back/{file}"))); - let ours = fs::read(format!( - "{}/../../skills/welcome-back/{file}", - env!("CARGO_MANIFEST_DIR") - )); - assert_eq!(on_lamp.unwrap(), ours.unwrap(), "{file}"); + let mut installed = Vec::new(); + for skill in install::PRODUCT_SKILLS { + let folder = skills_root().join(skill); + let files = skill_files(&folder); + assert!(files.contains(&"skill.toml".to_owned()), "{skill}"); + assert!(files.contains(&"SKILL.md".to_owned()), "{skill}"); + for file in files { + let path = format!("/var/lib/lampOS/skills/{skill}/{file}"); + let ours = fs::read(folder.join(&file)).unwrap(); + assert_eq!(fs::read(lamp.on(&path)).unwrap(), ours, "{path}"); + installed.push(path); + } + } + // Nothing else in the skills folder: no tests, no skill left out. + let mut placed: Vec = SETTINGS + .iter() + .map(|(path, _)| (*path).to_owned()) + .filter(|path| path.starts_with("/var/lib/lampOS/skills/")) + .collect(); + placed.sort(); + installed.sort(); + assert_eq!(placed, installed); + for (skill, _) in install::NOT_PRODUCT_SKILLS { + assert!( + !lamp.on(&format!("/var/lib/lampOS/skills/{skill}")).exists(), + "{skill}" + ); } assert!( lamp.host @@ -897,9 +970,23 @@ fn a_blank_lamp_gets_lamp_skills_with_welcome_back_and_greets_once() { .iter() .any(|line| line.starts_with("Environment=LAMP_SPEAK_FIRST=")) ); + // A due reminder and the news of a task waits up to 10 s for its skill's + // ask, then the voice says it itself: never both, never neither. Each + // set once, so no later line puts the voice's own timing back. + for switch in ["LAMP_SCHEDULE_SPEAK", "LAMP_TASK_NEWS"] { + let set: Vec<&str> = service + .iter() + .filter_map(|line| line.strip_prefix(&format!("Environment={switch}="))) + .collect(); + assert_eq!(set, ["skill"], "{switch}"); + } let skills = fs::read_to_string(lamp.on("/etc/systemd/system/lamp-skills.service")).unwrap(); assert!(skills.contains("--say-socket /run/lampos-say/say.sock")); assert!(skills.contains("--dir /var/lib/lampOS/skills")); + // Dance's tool reaches the model beside the actions part's, which the + // voice reads at each session. + assert!(skills.contains("--tools-dir /run/lampos-actions/skills")); + assert!(service.contains(&"Environment=LAMP_ACTIONS_DIR=/run/lampos-actions")); } /// #1390: the product's tasks part closes the harness each task started once diff --git a/lampOS/crates/voice/src/coordinator.rs b/lampOS/crates/voice/src/coordinator.rs index 4f8f1e785e..ae0c26fad2 100644 --- a/lampOS/crates/voice/src/coordinator.rs +++ b/lampOS/crates/voice/src/coordinator.rs @@ -8500,7 +8500,8 @@ fn reminder_waits(held: &Option<(String, u64)>, offered: Option<&crate::reminder } /// [`for_skill`] for the news first in line. Progress is the Policy's to -/// time already, and is told as it was. +/// time already, and is told as it was. News waits for its skill from when +/// the voice had it ([`hold_from`]). fn news_for_skill( news: &mut crate::task_news::TaskNews, says: Option<&mut crate::say::Says>, @@ -8510,6 +8511,11 @@ fn news_for_skill( let (key, progress, task) = news .waiting() .map(|front| (front.key.clone(), front.progress.is_some(), front.task()))?; + let now_us = monotonic_us(); + let found_us = news.found_at(&key).map(|found| { + now_us.saturating_sub(u64::try_from(found.elapsed().as_micros()).unwrap_or(u64::MAX)) + }); + hold_from(held, &key, found_us); for_skill( says, crate::say::Source::Tasks, @@ -8517,10 +8523,27 @@ fn news_for_skill( &key, task, held, - monotonic_us(), + now_us, ) } +/// `key`'s wait for its skill's ask counted from `found_us`, when the voice +/// had it, and not from when it could first be said, unless it is held +/// already (the reviewer's P2 on #1667). The Task news skill asks the +/// moment work settles, and an ask lives `say::FROM_LIVES_US`: news held +/// back past that by nobody there or by a talk would otherwise wait 10 s +/// more, in silence, for an ask that never comes again. So it is told as +/// soon as it can be, by the voice; within those 10 s, with the skill's +/// ask, as before. A reminder is held from when it can be said, since +/// Reminders asks only then (`skills/reminders`, `voice/state` resting). +fn hold_from(held: &mut Option<(String, u64)>, key: &str, found_us: Option) { + if let Some(found_us) = found_us + && held.as_ref().is_none_or(|(held_key, _)| held_key != key) + { + *held = Some((key.to_owned(), found_us)); + } +} + /// The news this turn tells, its own key and those told with it, put on the /// turn: handed to the tasks part only once it is heard whole (#1390). fn news_on(told: crate::task_news::News, reply: &mut Option) { diff --git a/lampOS/crates/voice/src/coordinator/tests/say.rs b/lampOS/crates/voice/src/coordinator/tests/say.rs index 33b99a95d2..c86ad549da 100644 --- a/lampOS/crates/voice/src/coordinator/tests/say.rs +++ b/lampOS/crates/voice/src/coordinator/tests/say.rs @@ -537,6 +537,114 @@ fn news_takes_only_an_ask_for_its_own_task() { assert_eq!(answered.try_recv(), Ok(Answer::Done(Outcome::Spoken))); } +/// The reviewer's P2 on #1667: news waits for the Task news skill from when +/// the voice had it, the moment the skill asks, and not from when it could +/// first be told. Settled while nobody was there and first told 30 s later, +/// when someone is back, the skill's ask is long gone: the voice tells it +/// at once, not 10 s later still. Had 3 s before it can be told, it waits +/// out the rest of the 10 s, from when it was had however often it is held +/// again, and the skill's ask is taken when it comes. A reminder, nothing +/// had for it, waits all 10 s from when it can be said. +#[test] +fn news_waits_for_its_skill_from_when_the_voice_had_it() { + use crate::say::{SKILL_WAITS_US, Says, Source}; + let had = 1_000_000; + let mut idle = Says::idle_for_test(); + let news = |says: &mut Says, key: &str, task, now_us, held: &mut Option<(String, u64)>| { + hold_from(held, key, Some(had)); + for_skill(Some(says), Source::Tasks, true, key, task, held, now_us) + }; + let mut held = None; + assert!(matches!( + news(&mut idle, "s:9:1", Some(9), had + 30_000_000, &mut held), + Some(None) + )); + assert_eq!(held, None); + assert!(news(&mut idle, "s:10:1", Some(10), had + 3_000_000, &mut held).is_none()); + // Held again on later ticks, and had later by the caller's count: the + // wait keeps its start. + hold_from(&mut held, "s:10:1", Some(had + 5_000_000)); + assert_eq!(held, Some(("s:10:1".to_owned(), had))); + assert!( + for_skill( + Some(&mut idle), + Source::Tasks, + true, + "s:10:1", + Some(10), + &mut held, + had + SKILL_WAITS_US - 1 + ) + .is_none() + ); + assert!(matches!( + news( + &mut idle, + "s:10:1", + Some(10), + had + SKILL_WAITS_US, + &mut held + ), + Some(None) + )); + // Within its 10 s, the skill's ask for it is taken: the sway comes with + // the words. + let (mut says, answered) = + Says::asking_for_task_test(Source::Tasks, "task-news", Some(11), 60_000_000); + let ticket = news(&mut says, "s:11:1", Some(11), had + 5_000_000, &mut held) + .flatten() + .expect("11's ask, for 11's news"); + ticket.answer(Outcome::Spoken); + assert_eq!(answered.try_recv(), Ok(Answer::Done(Outcome::Spoken))); + // Nothing had: held from when it can be said, as a reminder is. + hold_from(&mut held, "s/1/1", None); + assert_eq!(held, None); + let reminder = |held: &mut Option<(String, u64)>, now_us| { + for_skill( + Some(&mut Says::idle_for_test()), + Source::Schedule, + true, + "s/1/1", + None, + held, + now_us, + ) + }; + assert!(reminder(&mut held, had + 30_000_000).is_none()); + assert!(reminder(&mut held, had + 30_000_000 + SKILL_WAITS_US - 1).is_none()); + assert!(matches!( + reminder(&mut held, had + 30_000_000 + SKILL_WAITS_US), + Some(None) + )); +} + +/// [`news_for_skill`] counts the wait from when the news came in +/// (`TaskNews::found_at`): in 30 s ago, told at once; in 3 s ago, it waits. +#[test] +fn news_first_in_line_waits_from_when_it_came_in() { + use crate::say::Says; + use std::time::Duration; + let news = |task: u64| crate::task_news::News { + key: format!("s1:{task}:1"), + note: "(News from work you handed over.)".into(), + until: None, + progress: None, + finished: None, + also: Vec::new(), + }; + let mut idle = Says::idle_for_test(); + let mut held = None; + let mut long_ago = + crate::task_news::TaskNews::waiting_for_test(news(9), Duration::from_secs(30)); + assert!(matches!( + news_for_skill(&mut long_ago, Some(&mut idle), true, &mut held), + Some(None) + )); + let mut lately = crate::task_news::TaskNews::waiting_for_test(news(10), Duration::from_secs(3)); + assert!(news_for_skill(&mut lately, Some(&mut idle), true, &mut held).is_none()); + assert!(held.is_some_and(|(key, _)| key == "s1:10:1")); +} + /// #938: an owner with nothing under way and listening ready, as at rest. fn resting_owner() -> Controller { let mut owner = Controller::new(new_boot().unwrap(), now()); diff --git a/lampOS/crates/voice/src/task_news.rs b/lampOS/crates/voice/src/task_news.rs index ec19f2be30..b180335326 100644 --- a/lampOS/crates/voice/src/task_news.rs +++ b/lampOS/crates/voice/src/task_news.rs @@ -244,6 +244,8 @@ pub struct TaskNews { offered: Receiver, told: Sender, waiting: VecDeque, + /// When each piece waiting came in, by key ([`Self::found_at`]). + found: BTreeMap, /// What the file says was told, as the reading thread last found it: /// news the person asked for and heard ([`crate::agent_tasks`]) is not /// told again. @@ -555,6 +557,7 @@ impl TaskNews { offered, told, waiting: VecDeque::new(), + found: BTreeMap::new(), told_already, stopping, status, @@ -577,6 +580,9 @@ impl TaskNews { pub fn waiting(&mut self) -> Option<&News> { while let Ok(news) = self.offered.try_recv() { if !self.waiting.iter().any(|waiting| waiting.key == news.key) { + self.found + .entry(news.key.clone()) + .or_insert_with(Instant::now); self.waiting.push_back(news); } } @@ -598,9 +604,41 @@ impl TaskNews { .is_none_or(|of| still_current(status.as_ref(), of)) }); } + // Only what still waits keeps when it came in. + let waiting = &self.waiting; + self.found + .retain(|key, _| waiting.iter().any(|news| news.key == *key)); self.waiting.front() } + /// When the piece of news `key` came in, while it waits: about when its + /// work settled, as the voice knows it ([`Self::waiting`] is called at + /// each tick of the voice's loop but while a reminder is held for its + /// skill). The Task news skill asks for it then, so the voice's wait + /// for that ask is counted from here (`coordinator`, the reviewer's P2 + /// on #1667). + pub fn found_at(&self, key: &str) -> Option { + self.found.get(key).copied() + } + + /// For a test of the voice's turn: `news` waiting, come in `ago`. + #[cfg(test)] + pub(crate) fn waiting_for_test(news: News, ago: Duration) -> Self { + let came = Instant::now() + .checked_sub(ago) + .expect("a clock that has run that long"); + Self { + offered: mpsc::channel().1, + told: mpsc::channel().0, + found: BTreeMap::from([(news.key.clone(), came)]), + waiting: VecDeque::from([news]), + told_already: Arc::new(Mutex::new(BTreeSet::new())), + stopping: Arc::new(AtomicBool::new(false)), + status: Arc::new(Mutex::new(None)), + heard: mpsc::channel().0, + } + } + /// Take the news waiting, as it is being told: it is not offered again. /// Progress is looked at once more first: one past its time, or whose /// task finished, went or moved on while the lamp waited for its moment @@ -2218,6 +2256,7 @@ mod tests { offered, told, waiting: VecDeque::new(), + found: BTreeMap::new(), told_already: Arc::new(Mutex::new(BTreeSet::new())), stopping: Arc::new(AtomicBool::new(false)), status: Arc::new(Mutex::new(None)), @@ -2246,6 +2285,7 @@ mod tests { offered, told, waiting: VecDeque::new(), + found: BTreeMap::new(), told_already: told_already.clone(), stopping: Arc::new(AtomicBool::new(false)), status: Arc::new(Mutex::new(None)), @@ -2281,6 +2321,35 @@ mod tests { assert!(news.waiting().is_none()); } + /// The reviewer's P2 on #1667: each piece keeps when it came in while it + /// waits, the first time only, however often it is offered again, and + /// lets it go once told. + #[test] + fn news_keeps_when_it_came_in_while_it_waits() { + let (mut news, offer, _heard) = waiting_news(); + let one = News { + key: "s:1:1".into(), + note: "(…)".into(), + until: None, + progress: None, + finished: None, + also: Vec::new(), + }; + let before = Instant::now(); + offer.send(one.clone()).unwrap(); + assert_eq!(news.waiting(), Some(&one)); + let came = news.found_at("s:1:1").expect("when it came in"); + assert!(came >= before && came <= Instant::now()); + std::thread::sleep(Duration::from_millis(5)); + offer.send(one.clone()).unwrap(); + assert_eq!(news.waiting(), Some(&one)); + assert_eq!(news.found_at("s:1:1"), Some(came)); + assert_eq!(news.found_at("s:2:1"), None); + assert_eq!(news.tell(), Some(one)); + assert!(news.waiting().is_none()); + assert_eq!(news.found_at("s:1:1"), None); + } + /// The task a piece of news is of, from its key, as each kind is made: /// a settled observation, a failed hand-over, and progress; a store /// with a colon in it is read from the right. @@ -2313,6 +2382,7 @@ mod tests { offered, told, waiting: VecDeque::new(), + found: BTreeMap::new(), told_already: Arc::new(Mutex::new(BTreeSet::new())), stopping: Arc::new(AtomicBool::new(false)), status: Arc::new(Mutex::new(None)), diff --git a/lampOS/deploy/product/lamp-skills.service b/lampOS/deploy/product/lamp-skills.service index 58272727ab..2818057a27 100644 --- a/lampOS/deploy/product/lamp-skills.service +++ b/lampOS/deploy/product/lamp-skills.service @@ -1,7 +1,9 @@ # lampOS skills: physical skills, each a folder in /var/lib/lampOS/skills (lampOS/docs/skills.md). -# The product unit (lampOS/docs/install.md); install puts Welcome back in that folder (#811). -# A skill speaks only through the voice's say service (LAMP_SAY=on in lamp-voice.service, its -# socket in the group lampos); the voice's own hello is off there, so only Welcome back greets. +# The product unit (lampOS/docs/install.md); install puts every product skill in that folder +# (lamp-update's install::PRODUCT_SKILLS: Welcome back, Reminders, Task news, Stretch break, +# Stuffy air and Dance). A skill speaks only through the voice's say service (LAMP_SAY=on in +# lamp-voice.service, its socket in the group lampos); the voice's own hello is off there, so only +# Welcome back greets, and its reminders and task news wait for their skills' asks (=skill). # LAMP_SKILLS_ASK=skip acts without asking the Policy, whose gate is not built yet (#939); # without it every skill is asked about and refused, and nothing is done. [Unit] diff --git a/lampOS/deploy/product/lamp-voice.service b/lampOS/deploy/product/lamp-voice.service index f597d63767..00c215142d 100644 --- a/lampOS/deploy/product/lamp-voice.service +++ b/lampOS/deploy/product/lamp-voice.service @@ -114,8 +114,14 @@ Environment=LAMP_TASKS_DIR=/run/lampos-tasks # The tasks part runs only with Harness (its unit's condition): without it the voice says work # can't be handed on yet, not that a part failed (#811). Environment=LAMP_TASKS_NEEDS=/usr/local/bin/harness +# News of work handed over is timed by the Task news skill (lampOS/skills/task-news): the voice keeps the +# words and tells each task once, with the skill's ask, or itself when no ask has come within 10 s (#1038). +# News of a task that stopped or asks something has no skill yet: the voice tells it after those 10 s. +Environment=LAMP_TASK_NEWS=skill Environment=LAMP_SCHEDULE_TOOLS=on -Environment=LAMP_SCHEDULE_SPEAK=on +# A due reminder is timed by the Reminders skill (lampOS/skills/reminders): the voice keeps its words and +# its acknowledgement, and says it itself when no ask has come within 10 s, so none is lost (#939). +Environment=LAMP_SCHEDULE_SPEAK=skill Environment=LAMP_SCHEDULE_SOCKET=/run/lampos-schedule/call.sock Environment=LAMP_SCHEDULE_DELIVERY_DIR=/var/lib/lampOS/voice/schedule-delivery Environment=LAMP_MEMORY_SOCKET=/run/lampos-memory/memory.sock diff --git a/lampOS/docs/install.md b/lampOS/docs/install.md index 42037b61ee..9caa37fab0 100644 --- a/lampOS/docs/install.md +++ b/lampOS/docs/install.md @@ -95,7 +95,7 @@ differs. Folders are what the unit makes or names. | selftest | `lamp-selftest --occasion first-boot` (a timer: 5 min after the lamp starts, then every 6 h; #812) | lampos, `CAP_SYSLOG` | `/var/lib/lamp-selftest/` (its own `StateDirectory`) | none | the bus (`lamp/selftest`), its runs' reports | | update | `lamp-update serve` | root | `/var/lib/lampos-update/` (0700) | `/etc/lampOS/update/key.pub` | none | | keeper | `lamp-keeper serve` | root | its service data folder | none | `lampos.target.d/keeper.conf`; the bus root | -| skills | `lamp-skills` | lampos (product unit, #811) | none | the skill folders, `/var/lib/lampOS/skills/` (Welcome back from install) | its tools, `/run/lampos-actions/skills/` | +| skills | `lamp-skills` | lampos (product unit, #811) | none | the skill folders, `/var/lib/lampOS/skills/` (every product skill from install) | its tools, `/run/lampos-actions/skills/` | | surround | `lamp-surround serve` | `@SURROUND_USER@` | not read for this page | `/etc/lampos/surround.json` (lower case, unlike every other part) | not read | The bus root, `/run/lampos-bus`, is made by the keeper (root, the parts' @@ -191,6 +191,48 @@ set, but for `LAMP_SPEAK_FIRST`: the product's hello is the Welcome back skill (`lamp-skills`, #811), spoken through the voice's say service, so the voice's own hello is off, as lamp-4ace now has it too. +**19 October: every behaviour that has a skill runs as the skill** (the +owner: "everything is a skill"). Install puts every product skill in +`/var/lib/lampOS/skills/` (`install::PRODUCT_SKILLS` in `crates/update`): +Welcome back, Reminders, Task news, Stretch break, Stuffy air and Dance, +each folder whole but for its `tests/`. The voice's own paths for them give +way in the product's `lamp-voice.service`: its hello stays off +(`LAMP_SPEAK_FIRST` unset), and `LAMP_SCHEDULE_SPEAK=skill` and +`LAMP_TASK_NEWS=skill` make a due reminder and the news of a task wait up +to 10 s for the skill's ask (`from = "schedule"`, `from = "tasks"`). The +voice still keeps the words: it says each once, with the skill's ticket +when the skill asked, so the lamp looks up, sways or droops with them, or +by itself when no ask came. Each skill asks once, and an ask is gone 20 s +later (`say::FROM_LIVES_US`), so the two are timed to meet (the reviewer's +P2 on #1667). Reminders asks only while the voice rests (`voice/state`), +keeping a reminder due during a talk until the talk ends (`wait`), when +the voice can say it too: said then, with the look. Task news asks the +moment the work settles (its per-task trigger cannot wait), so the voice +counts its 10 s from when it had the news, not from when it can first tell +it: news that settled while nobody was there, or more than 20 s before a +talk ended, is told at once when it can be, by the voice, without the sway. +The voice says a reminder itself 10 s after it can when `lamp-skills` is +down, or when the ask came too early: at a pause after which the talk went +on for over 20 s, or while the voice rested but could not say it (paused +or muted: what `voice/state` says then was not checked). It says news that +stopped or asks something, which no skill takes, 10 s after it came in. Left out, with +why (`install::NOT_PRODUCT_SKILLS`): Desk tutor, which reads handwriting +from the full look (`LAMP_LOOK_FULL=on` with `lamp-eyes --full-look`) that +the product doesn't run, and its marks, called only after its look. A test +fails on a folder in `lampOS/skills/` that is in neither list. With them a +product lamp tells each session 31 functions of the 32 a session takes +(`lamp_gemini::MAX_TOOLS`): the voice's own 13, the actions part's 17 and +Dance's (`crates/update/tests/functions.rs`, which fails if one is left out +or the count changes). The voice leaves out by order, without a word, what +is past 32, so Desk tutor's two would push its marks' `show_verdict` out. lamp-4ace's +own unit keeps `LAMP_SCHEDULE_SPEAK=on` and the voice's own news until the +skills have run there; the voice's own code for them is removed after. +Install only writes skills, never removes one: a skill later moved out of +`PRODUCT_SKILLS`, or a file dropped from a skill's folder, stays on every +lamp installed before and keeps running. Taking a skill out is the removal +work's (`lamp-skills remove` and the core and installable kinds, on +`first/skills-core-installable`), not install's. + **17 October: lamp-4ace's switches of that day became the product's** (joining.md's rule), taken from lamp-4ace's unit as it ran that day; most were switched on there on 16 or 17 October, several only as part of the groups of #1238's census, so their lamp record is that day's running, not a run of their own. They're the switch groups (lead-in to a tool, a waited answer kept, the stop told, the asker first only recorded, names checked, the staff note, task news at a pause, memory rules), the light requests (the voice's ring and the skills' half, `lamp-skills.service`), the quiet pace, starting offline, the asleep ring, a sorry in another language, the lamp's name in passing ignored, first words kept, tasks asked and who asked, a cut keeping the name, the self-test's speaker, the stall recorder, the strong ring eye, and short sounds judged in a quiet room after a check. Not taken: those that need the second voice's configuration (stop words, its down-line and wait cue, the voice link), which a product lamp doesn't have; the office's own (its Harness target, its say group); head touch, which needs Controls' and Expression's flags as well (#711); those that need another part's flag the product doesn't pass (the Policy holding actions needs `lamp-policy --act attention`, its hello and progress news need `--hello` and `--progress`, the full look needs the eyes' `--full-look`, a restored mute needs `--controls-mute`); and "that was weird", a builder's review tool whose marks (`/var/lib/lampOS/voice/marked/`) the reset doesn't wipe. Expression's catalogue differs too (`expression-alive-name-speech` on lamp-4ace, `expression-alive` on lamp-52e6): the product takes lamp-4ace's. @@ -388,9 +430,9 @@ As root, on a lamp with the base image (`crates/update/src/install.rs`): a link: a link is left as it is (#1293). A lamp's own setting found closed to other users is kept as it is and said: `PATH is closed to other users (600): the parts run as lampos cannot read it`. The product settings (Expression's catalogue, the - controls' two files, and the first skill, Welcome back, in - `/var/lib/lampOS/skills/welcome-back/`: its `skill.toml`, `SKILL.md` and - light, not its tests) are written only where one is missing or is still + controls' two files, and the product skills, each in + `/var/lib/lampOS/skills//`: its `skill.toml`, `SKILL.md` and + lights, not its tests) are written only where one is missing or is still exactly what an install wrote before, by the SHA-256 kept for it in `state.json`, so a newer build's default reaches a lamp that never changed it. A setting changed on the lamp since, or there before any @@ -669,8 +711,11 @@ lamp-update check-units --cat units.txt --product lampOS/deploy/product unit texts (lamp-4ace's names `/opt/lampOS/records/`, the crate's `/opt/lampOS/bin/`), while `deploy/units/README.md` says it has no unit on any lamp. -- `lamp-skills` (#811, from lamp-06c1): installed with Welcome back and the - voice's say service (`LAMP_SAY=on`, its socket in the group `lampos`), the - voice's own hello (`LAMP_SPEAK_FIRST`) off so the lamp greets once, and +- `lamp-skills` (#811, from lamp-06c1): installed with every product skill + (since 19 October) and the voice's say service (`LAMP_SAY=on`, its socket + in the group `lampos`), the voice's own hello (`LAMP_SPEAK_FIRST`) off so + the lamp greets once, its reminders and task news waiting for their + skills (`LAMP_SCHEDULE_SPEAK=skill`, `LAMP_TASK_NEWS=skill`), and `LAMP_SKILLS_ASK=skip` until the Policy's gate (#939). Only in tests: no - lamp has run the product unit yet. + lamp has run the product unit with them yet, and Reminders and Task news + have not run as skills on any lamp. diff --git a/lampOS/docs/schedule.md b/lampOS/docs/schedule.md index c3d869f4f6..6347c49805 100644 --- a/lampOS/docs/schedule.md +++ b/lampOS/docs/schedule.md @@ -647,7 +647,9 @@ Actions only; installation and on-lamp qualification remain First's work. Voice requires exact `LAMP_SCHEDULE_SPEAK=on` (or `skill`, where a due reminder also waits up to 10 s for the Reminders skill to ask for it through -the say service, then is said by Voice itself: [speaking-first.md](speaking-first.md#asked-by-another-part-the-say-service-939)) +the say service, then is said by Voice itself; the skill asks only while +`voice/state` rests, so a reminder due during a talk is asked for when the +talk ends, as Voice says it, #1667: [speaking-first.md](speaking-first.md#asked-by-another-part-the-say-service-939)) (both values give the provider worker its `--schedule-speak`, by one rule, `reminders::speaks`: until 17 Oct `skill` gave the worker none, so it refused every reminder and none was said, the reviewer on #1038) diff --git a/lampOS/docs/skills.md b/lampOS/docs/skills.md index f9bcdc13c9..3f83921fdf 100644 --- a/lampOS/docs/skills.md +++ b/lampOS/docs/skills.md @@ -440,13 +440,21 @@ As built on 2026-10-16 (#938, `crates/skills`, not yet run on a lamp): tool's caller), which bound a peer that keeps its own time. `lamp-update install` puts it on a home lamp (#811): the product unit -`deploy/product/lamp-skills.service`, Welcome back in `/var/lib/lampOS/skills`, -and the product voice with the say service on and its own hello off. +`deploy/product/lamp-skills.service`, every product skill in +`/var/lib/lampOS/skills` (since 19 October: Welcome back, Reminders, Task +news, Stretch break, Stuffy air and Dance, `install::PRODUCT_SKILLS`; Desk +tutor and its marks wait for the full look in the product, +`install::NOT_PRODUCT_SKILLS`), and the product voice with the say service +on, its own hello off, and its reminders and task news waiting for their +skills (`LAMP_SCHEDULE_SPEAK=skill`, `LAMP_TASK_NEWS=skill`; install.md). +A skill with a tool costs one of the 32 functions a session takes, after +the voice's own and the actions part's, and what is past 32 is left out +without a word: a product lamp's are 31 (`crates/update/tests/functions.rs`). `deploy/units/lamp-skills.service` stays the template for a lamp set up by hand. The product unit passes `--zone system`, the zone setup saved (#820), across summer time (#1384). lamp-4ace's passes neither yet, so there a skill with -`[quiet]` hours never starts itself. Neither puts Stuffy air in its -folder today (install puts only Welcome back on a home lamp). +`[quiet]` hours never starts itself. Install puts Stuffy air on a home lamp; +lamp-4ace's folder doesn't have it. lamp-4ace's folder carries `lamp-skills.service`, and its voice unit has the voice's own hello (`LAMP_SPEAK_FIRST`) off and the say service on, so only one greets. Elsewhere the voice's hello (`speak_up.rs`, Arrival) still @@ -734,9 +742,9 @@ where it goes: | Behaviour today | Where it lives now | Becomes | |---|---|---| -| Hello when someone comes back to the desk | The voice (`speak_up.rs`), asking the Policy (#883) | Skill: Welcome back | -| A reminder or timer coming due | The voice's reminder consumer | Skill: Reminders (setting one stays a voice tool) | -| News of a task handed to Claude Code or Codex, and progress news | The voice (`task_news.rs`), the Policy's progress act | Skill: Task news | +| Hello when someone comes back to the desk | The Welcome back skill on a product lamp and on lamp-4ace; the voice's own (`speak_up.rs`, asking the Policy, #883) is off on both and only records when one was due | Skill: Welcome back | +| A reminder or timer coming due | The Reminders skill on a product lamp (`LAMP_SCHEDULE_SPEAK=skill`), asking while the voice rests, so one due during a talk is said when it ends, with the look: the voice keeps the words and says it itself when no ask comes within 10 s. The voice's reminder consumer, timing it too, on lamp-4ace (`on`) | Skill: Reminders (setting one stays a voice tool) | +| News of a task handed to Claude Code or Codex, and progress news | The Task news skill on a product lamp for done and failed (`LAMP_TASK_NEWS=skill`): the voice keeps the words and tells it itself when no ask comes within 10 s of having it (at once, without the sway, for news that settled while nobody was there: the skill's ask is gone 20 s after the work settles, and an item's run cannot wait), as it does stopped and asking news, which no skill takes. The voice (`task_news.rs`) on lamp-4ace; progress news is the Policy's act, off in the product | Skill: Task news | | Sleep at night, waking in the morning | Expression's catalogue (`alive`) | Skill: Bedtime and Morning, later | | Turning to a voice, looking away to think, back before its first word, nods while speaking | Voice, body, Expression | Built in: the conversation, under a second | | A tap on the head, a hand held on it, three seconds to sleep | Controls, Expression, voice (#711, #849) | Built in: a reflex, under 150 ms | @@ -755,8 +763,8 @@ library as skills. | Skill | Trigger | What it does | Replaces | |---|---|---|---| | Welcome back | A face arrives after 5 minutes with nobody | Policy; turns to them, the light warms, a short hello, by name if known | The hello (#883, `speak_up.rs`) | -| Reminders | `schedule/due` | Looks at the person, says it: `say = { from = "schedule" }`, the voice keeping the words and the acknowledgement (#939, (c); `skills/reminders`). The ring's pulse waits for a light step that can pulse | The reminder consumer's timing in the voice (`LAMP_SCHEDULE_SPEAK=skill`); its delivery stays there | -| Task news (#1021) | `tasks/status`: a task comes into `settled` (#1048) as `idle` (done) or `failed`, each task on its own | Policy (`news`); done: looks at the person if a face is in view, `happy`, and the voice's own news of it (`say = { from = "tasks" }`); failed: `caring`, its own light a step dimmer until the droop and the words end, and the news, asking whether to try again. No red ring | Task news in the voice (`task_news.rs`), once it has done 5 of 5 on lamp-4ace with the voice's news off; until then both exist and the skill's folder is not installed. Written by jarvis; the per-item trigger (`each`), steps by trigger (`on`) and the waiting of item runs were added to `lamp-skills` for it. Its cases are replays; it has not run on a lamp | +| Reminders | `schedule/due`, while the voice rests (`wait`: one due during a talk is asked for when it ends) | Looks at the person, says it: `say = { from = "schedule" }`, the voice keeping the words and the acknowledgement (#939, (c); `skills/reminders`). The ring's pulse waits for a light step that can pulse | The reminder consumer's timing in the voice (`LAMP_SCHEDULE_SPEAK=skill`, on a product lamp since 19 October); its delivery stays there | +| Task news (#1021) | `tasks/status`: a task comes into `settled` (#1048) as `idle` (done) or `failed`, each task on its own | Policy (`news`); done: looks at the person if a face is in view, `happy`, and the voice's own news of it (`say = { from = "tasks" }`); failed: `caring`, its own light a step dimmer until the droop and the words end, and the news, asking whether to try again. No red ring | The timing of task news in the voice (`task_news.rs`): on a product lamp since 19 October, installed with `LAMP_TASK_NEWS=skill`, so the voice waits up to 10 s for its ask and tells each task once, its words staying the voice's; the voice's own timing is removed once it has done 5 of 5 on lamp-4ace. Written by jarvis; the per-item trigger (`each`), steps by trigger (`on`) and the waiting of item runs were added to `lamp-skills` for it. Its cases are replays; it has not run on a lamp | | Stretch break | Someone at the desk for 50 minutes | Stretches itself as an invitation, never a lecture (creature.md) | New | | Stuffy air (#1021) | `air/state`'s `co2_ppm` above 1,200 for 10 minutes | Droops once (`sleepy`) and dims a shade until the air is under 1,200 again; once a spell, again only after 10 minutes under 1,000; never from 22:00 to 08:00 (a spell that holds through the night droops once at 08:00), nor while someone talks to it; the dim also ends 15 minutes after the sensor falls silent; never speaks of it (the voice's air facts do, when spoken to) | New: reworked by jarvis from ten's; the rearm line, the quiet hours and the dim held until the trigger ends were added to `lamp-skills` for it. Its cases are made by hand (lamp-4ace's sensor is flaky: it answers, with 20 to 30 failed reads a minute). It ran on lamp-06c1 once with the air fed by hand (`docs/verification/stuffy-air-lamp-06c1-20261017/`). No lamp with a working sensor has run it: lamp-06c1's and lamp-cb94's never answer on I2C bus 0 (#705, 17 Oct) | | Dance (#999) | "Lamp, dance", "show me a dance", "dance to this": its tool, `dance(style, long)` | One short playful line, then, when the line ends, one of V1's five dances (#979) picked by the mood asked for (calm: chill or waltz; upbeat: groove or hype; jazz), played twice for a long one, with a low warm glow given back at its end. Never on its own: the ears cannot yet tell music from talk | New: written by jarvis; its tool's arguments, the body's clips by name and the steps after the say were added to `lamp-skills` for it. Its cases are replays; it has not run on a lamp | diff --git a/lampOS/docs/speaking-first.md b/lampOS/docs/speaking-first.md index 73058e078b..29aa0cc605 100644 --- a/lampOS/docs/speaking-first.md +++ b/lampOS/docs/speaking-first.md @@ -418,8 +418,12 @@ for `say = { from = "schedule" | "tasks" }` ([skills.md](skills.md)). before. `task` goes only with `from: tasks`. Without it, task 9 done and task 10 failed in one message, asked for in the order 10, 9, drooped and dimmed the lamp while it said 9 was done (the reviewer on #1042). - With `LAMP_TASK_NEWS=skill`, news waits its 10 s for an ask for its own - task, then is said by the voice. With `LAMP_TASK_NEWS_AT_PAUSE=on`, news + With `LAMP_TASK_NEWS=skill`, news waits for an ask for its own task + until 10 s after the voice had it (`TaskNews::found_at`), then is said + by the voice: the Task news skill asks the moment the work settles, and + its ask is gone 20 s later, so news held back longer (nobody there, a + talk) is said at once when it can be, not 10 s later still (the + reviewer's P2 on #1667). With `LAMP_TASK_NEWS_AT_PAUSE=on`, news told with a skill's ask, or while another such ask waits, is told one at a time, not together (the reviewer on #1223): each run takes its own task's news. @@ -433,7 +437,7 @@ for `say = { from = "schedule" | "tasks" }` ([skills.md](skills.md)). it got that only for `on`, refused every reminder under `skill`, and none was said (the reviewer on #1038). With **`LAMP_TASK_NEWS=skill`** news of work finished, stopped or asking waits - the same way; progress is the Policy's to time already and is told as it + the same way, but from when the voice had it (above); progress is the Policy's to time already and is told as it was. Without them nothing waits, and a `from` ask that is waiting is taken whenever its source next says one, so the skill still moves with the words. With the switch but no say service (`LAMP_SAY` off), nothing diff --git a/lampOS/docs/switches.md b/lampOS/docs/switches.md index 9116cc23ad..4b8b6cd084 100644 --- a/lampOS/docs/switches.md +++ b/lampOS/docs/switches.md @@ -123,7 +123,7 @@ motion, since lamp-4ace's motors stay still until Monday: `LAMP_STAFF_NOTE` | LAMP_RING_ASLEEP | `on`; off. Faint warm breathing ring while muted/asleep, not dark | — | on (#1332) | #1025 | Goes on with the stop fix (#1024), sleep by a hand on the head, photographed; no result in docs | off until the photographed sleep check | Rule-8 friendly (a dark ring reads as broken) but no lamp result recorded | until | | LAMP_REMINDER_PULSE | `on` only; off. One soft pulse a minute on the asleep face while a reminder is owed and the switch or a mute holds the microphones (not asleep), the first at its due time, for half an hour from then at most; needs LAMP_RING_ASLEEP, and without it says so once in the journal | — | — | #1545 follow-up | lamp-4ace: mute, a reminder due, the pulse filmed once a minute and gone when said or cancelled; a hand on the head shows none | off until that check | ring-choreography.md, "The asleep face"; not shown on a lamp | until | | LAMP_RING_EYE | `on`/`strong`; off. Ring looks toward the person and blinks at rest | on | strong (#1332) | commit d3e9cc3d2 (direct) | ring-choreography.md: lean "not yet seen on a lamp"; the owner found it too subtle 15 Oct, hence `strong` | on (already) | Running on lamp-4ace; the product unit sets `strong` since #1332 | on | -| LAMP_SAY | `on`; off. Say socket for other parts (skills) to speak through the voice | on | on | #954 | #954 steps: begun/spoken, dropped on speech-over, expired under pause; not recorded as shown | on (already) | Welcome back (lamp-skills, in product) speaks only through it (install.md) | on | +| LAMP_SAY | `on`; off. Say socket for other parts (skills) to speak through the voice | on | on | #954 | #954 steps: begun/spoken, dropped on speech-over, expired under pause; not recorded as shown | on (already) | Every product skill (lamp-skills: Welcome back, Reminders, Task news, Dance) speaks only through it (install.md) | on | | LAMP_SAY_GROUP | a group name; unset = socket private to the voice's user | orangepi | lampos | #954 | Same as LAMP_SAY (socket as `orangepi` on lamp-4ace) | lampos (already) | lamp-skills runs in group lampos in product; orangepi is lamp-4ace's user | on | | LAMP_SAY_SOUNDS | absolute dir; unset = /var/lib/lampOS/skills. Skill sounds only from under it | — | — | #1144 | #1144: "nothing has run on a lamp, and no sound has been heard" | unset (default matches install's skills folder) | The default is the product's skills folder; a path, not a behaviour switch | setting | | LAMP_SECOND_DOWN_LINE | `on`; off. Backup voice says "I can't think right now" when chat never starts | — | — | #1195 | none named (from the relay outage at 02:29, seen on lamp-52e6) | off until heard on a lamp with the relay's chat down | #1195 has host tests only; said unasked it must not read as a broken machine (second-voice.md) | until | @@ -176,7 +176,7 @@ motion, since lamp-4ace's motors stay still until Monday: `LAMP_STAFF_NOTE` | LAMP_PROGRESS_NEWS | `on`; off. Voice tells a fresh Policy `say_now` progress step as news | — | — | #765 | tasks.md "Playing progress news on lamp-4ace": a few-minute task, expect `say_now` then one spoken sentence; PR says nothing run on a lamp | off until that play is done, with `lamp-policy --act progress` | #765 "nothing was run on a lamp"; tasks.md most likely wrong: the Policy may `wait` on everything | until | | LAMP_PROVIDER_WRITES | `patient` only; off. A write to Gemini may wait on a link that is behind: each write 1 s instead of 200 ms, 8 ms more for each KiB (the look's picture about 3 s, a camera frame about 1.1 s), and an input queue of 128 blocks instead of 32, so 640 ms of speech waiting behind a write doesn't end the connection as `Backpressure` (gemini-session-reliability.md) | — | — | #705 (17 Oct) | Tests only: on lamp-4ace 105 `write_timeout` drops since about 12 Oct lost 84 lines; which write timed out is named once #1393 is on a lamp | off until a battery on lamp-4ace with it shows fewer lines lost, counting `write_timeout`, `barrier_timeout` and `Backpressure` together (a drop can move between them), and answers not late | While a write waits nothing is read, so an answer's audio behind it comes late; unplayed | until | | LAMP_ROOM_FACTS | `on` only; off. Model told when the room goes quiet or busy | on | on | commit f7598521b (direct) | none named in a PR; room_told.rs and attention.md cite lamp-4ace scenes 13 to 16 Oct | on (already) | On in both units; the rule behind lamp-4ace's busy-office fixes (attention.md) | on | -| LAMP_SCHEDULE_SPEAK | `on` or `skill` (waits up to 10 s for a skill's ask); off. Speaks due reminders | on | on | #747 | schedule.md's Task 31 lamp qualification (seven spoken cases); for `skill`, #1038's reminder-in-two-minutes with/without lamp-skills | on (already); `skill` off until #1038's lamp check | Installed on lamp-4ace (install.md); the `skill` value's 10 s wait is #1038's "most likely wrong", no lamp result recorded | on | +| LAMP_SCHEDULE_SPEAK | `on` or `skill` (waits up to 10 s for a skill's ask); off. Speaks due reminders | on | skill (19 Oct, with the Reminders skill installed) | #747 | schedule.md's Task 31 lamp qualification (seven spoken cases); for `skill`, #1038's reminder-in-two-minutes with/without lamp-skills | `skill` in the product (already); lamp-4ace `on` until #1038's lamp check | The owner, 19 Oct: every behaviour that has a skill runs as the skill (install.md); Reminders asks only while `voice/state` rests, so a reminder due during a talk is said when it ends, with the look (#1667); with no ask in 10 s the voice says it itself, so none is lost; the 10 s wait is #1038's "most likely wrong", no lamp result recorded | on | | LAMP_SCHEDULE_TOOLS | `on` only; off. Permits the six Schedule tools (voice and actions) | on (voice, actions) | on (voice, actions) | #747 | schedule.md's Task 31 lamp qualification: off-state minute, scheduling checks, seven spoken cases | on (already) | On lamp-4ace and listed by install.md as product default | on | | LAMP_SESSION_FACT | `on` only; off. Tells each new session nobody is talking to the lamp | on | on | commit 0b042f8fd (direct) | none named; RUNBOOK: on since 13 Oct 17:29, "unmeasured" | on (already) | On since 13 Oct; session_fact.rs: its effect on a session's first hearing still unmeasured | on | | LAMP_SHORT_DONE | `on` only; off. lamp-actions tells the model to say only a word or two after a light or volume change it asked for | — | — | #1520 | lamp-4ace: the battery's light and volume turns, the model's audio after our answer and the reply's length, before and after in the same hour | off until that check | The change is the answer (actions.md); a light turn's change at about 0.94 s (0.88 + 0.06 s medians), its sentence after another 0.80 s; an "already" answer is never short | until | @@ -187,7 +187,7 @@ motion, since lamp-4ace's motors stay still until Monday: `LAMP_STAFF_NOTE` | LAMP_TASKS_TARGET | alias string; default `office`. The tasks target the work goes to | — | — | commit c2ac775f5 (direct) | none of its own; LAMP_TASKS_WHO's lamp-4ace minute used `office-claude` | the lamp's own alias where its `targets.json` names differ; unset (`office`) otherwise | A per-lamp name, not a behaviour | setting | | LAMP_TASKS_TOLD | file path; default /var/lib/lampOS/voice/tasks-told. Record of task news already told | — | — | commit 55e92e3d8 (direct) | none named | unset (default path) | A storage location, not a behaviour switch (tasks.md, setup.md) | setting | | LAMP_TASKS_WHO | `on` only; off. The person may name Claude Code, Codex or OpenCode | — | on (#1332) | #769 | Its own minute on lamp-4ace: #769, 15 Oct 17:47–17:49, with `LAMP_TASKS_TARGET=office-claude`. **Newer, with it on and failing:** 16 Oct about 21:05 and 22:51 (stack36, with LAMP_TASKS_ASKED), a Claude Code poem and a Codex haiku, each asked for by name, were done by the lamp itself with no `start_agent_task` (tasks.md) | off until LAMP_STAFF_NOTE's count shows named hand-overs landing on lamp-4ace | Its newest records with it on fail; the miss is the model's (what LAMP_TASKS_ASKED and LAMP_STAFF_NOTE are for), but switching it on alone doesn't make a named hand-over land | until | -| LAMP_TASK_NEWS | `skill`; otherwise as before. Finished/stopped/asking news waits up to 10 s for a skill's ask | — | — | #1038 | #1038: lamp-tasks --progress, LAMP_SAY, Task news skill folder; news said once via the skill; no record shown | off until #1038's task-news-through-the-skill check is played, after #1237 and #1042 send `task` | Needs the Task news skill (#1042); until a news ask names its task (#1237), a look or dim can go with the wrong task when two settle together (the Second) | until | +| LAMP_TASK_NEWS | `skill`; otherwise as before. Finished/stopped/asking news waits for a skill's ask until 10 s after the voice had it (#1667), then is told by the voice | — | skill (19 Oct, with the Task news skill installed) | #1038 | #1038: lamp-tasks --progress, LAMP_SAY, Task news skill folder; news said once via the skill; no record shown | `skill` in the product (already); lamp-4ace off until #1038's task-news-through-the-skill check | The owner, 19 Oct: every behaviour that has a skill runs as the skill (install.md); a news ask names its task (#1237), so a look or dim goes with its own; the voice tells each piece once, itself when no ask came within 10 s of having it: at once on someone's return for news settled while nobody was there (the skill's ask is gone after 20 s, so the sway is lost then), and stopped and asking news, which the skill doesn't take | on | | LAMP_TASK_NEWS_AT_PAUSE | `on`; off. News at the first 1 s pause instead of after 15 s of quiet; since #1223 tasks finished together are told in one turn, at most three | — | on (#1332) | #1161 | lamp-4ace: talk while a task runs; news at the first ~1 s pause, 3 s after the lamp's words, under 20 s; not shown | off until #1161's lamp-4ace check, which also covers #1217, #1223 and #1231 (one play: news at the first 1 s pause; `since_turn_ms` about 3,000 after an unanswered reply; two tasks within a minute told in one turn) | #1161 names rule 8 (news landing between two people talking) as most likely wrong; no lamp result recorded | until | | LAMP_TIME_PLACES | `on` only, with LAMP_CLOCK; off. `the_time` takes a place (an IANA zone such as `Europe/London`, or a city's name alone looked for in the usual areas), read from `/usr/share/zoneinfo` with the voice's own zone-file reader (its closing rule worked out for a slim file), and answers that place's time with the lamp's own beside it; a place it has no zone for is said to be unknown, never given the lamp's time; the opening note says whose time it gives | — | on (#1547, 18 Oct) | #1547 | **Shown** on lamp-4ace 18 Oct 19:41–19:45 (`LAMP_TIME_PLACES=on`, #1552's build): 8 of 8 right against each place's clock read there with `TZ=… date` (London twice, New York twice, Tokyo twice, here twice; #1547). Jarvis's 64 DST moments, fat and slim zone files, match Python's `zoneinfo` (#1575). First audio with and without it not measured | on | On 18 Oct 16:40 "what time is it in London" was answered with the lamp's own, Ho Chi Minh's; with it the model must still pick the zone, and a city it names wrongly gets that zone's time | on | | LAMP_TOOLS | `on` only; off. Model may call not_for_me instead of speaking | on | on | commit 995240456 (direct) | none named | on (already) | On in both units since the function-call routing landed (tools.md) | on | @@ -311,7 +311,8 @@ Gaps between the docs, the units and the code, for whoever owns each: speaking (`reminders.rs`), but only `on` passes `--schedule-speak` to the provider worker (`tools.rs`), so the worker defers every due reminder as disabled (`provider_worker.rs`, `reminder_policy.rs`). It came in with - #1038, and agrees with that row's "`skill` off". + #1038. Fixed since: the worker gets `--schedule-speak` under `skill` too + (`tools::tests::schedule_speech_by_skill_gives_the_worker_its_flag`). - **`LAMP_KERNEL_CLOCK` never reached the provider worker:** it is spawned with `env_clear()` and the switch isn't passed on (`process.rs`), so the worker's `the_time` and session note never saw the clock unset (Ten on diff --git a/lampOS/docs/tasks.md b/lampOS/docs/tasks.md index a4d08c12a8..3f0660de76 100644 --- a/lampOS/docs/tasks.md +++ b/lampOS/docs/tasks.md @@ -1067,8 +1067,11 @@ going?" now serves a page of over 9,000 bytes. The Task news skill (Jarvis, #1021) triggers on `tasks/status` and moves and lights with the news; the voice keeps the words and tells each piece once, as above. With `LAMP_TASK_NEWS=skill`, news of work finished, stopped or -asking waits for the skill's `from: tasks` ask on the say service, at most -10 s, and is then told with that ask's ticket, or by the voice itself +asking waits for the skill's `from: tasks` ask on the say service until +10 s after the voice had it, and is then told with that ask's ticket, or by +the voice itself (news that settled while nobody was there is told when +someone is, at once and without the skill's sway: its ask, made as the work +settled, is gone 20 s later; the reviewer's P2 on #1667) ([speaking-first.md](speaking-first.md#asked-by-another-part-the-say-service-939)). An ask that names its task (`"task":10`) is given that task's news only, so the skill's look and light go with its own task (the reviewer on #1042). diff --git a/lampOS/docs/updates.md b/lampOS/docs/updates.md index 145c9a1763..20ed6c31fa 100644 --- a/lampOS/docs/updates.md +++ b/lampOS/docs/updates.md @@ -336,8 +336,12 @@ in `docs/install.md` (step 9, and "Checking a lamp runs the product's units"). `install` puts lampOS on a blank lamp from the newest build: every product -part through its `current` link, the product units, users and zone, enabled -and not started ([install.md](install.md)). `check` never writes, whatever `--apply` says. `once` and `check` end with +part through its `current` link, the product units, users and zone, the +product settings and every product skill (`/var/lib/lampOS/skills/`), +enabled and not started ([install.md](install.md)). A round writes only +the units a build carries for this lamp by name, never the product's +settings or skills: a product lamp gets a new product skill or switch when +`lamp-update install` is run again. `check` never writes, whatever `--apply` says. `once` and `check` end with 0 (up to date, switched, or only saying), 1 (refused, put back, failed), 3 (the channel cannot be reached), 4 (no quiet moment); a wrong option with 2. `serve` prints a round's outcome only when it differs from the last. The diff --git a/lampOS/skills/reminders/SKILL.md b/lampOS/skills/reminders/SKILL.md index d0c918b2d7..e230941c2c 100644 --- a/lampOS/skills/reminders/SKILL.md +++ b/lampOS/skills/reminders/SKILL.md @@ -11,7 +11,11 @@ person, as someone at the next desk does before saying "your call's in five minutes", and says it. Nothing more: no gesture, no light. - **Trigger:** `schedule/due` shows a reminder due where none was, while - Schedule is on and ready. + Schedule is on and ready and the voice rests (`voice/state`): one due + while someone talks with the lamp is kept until the talk ends, and asked + for then, at Schedule's next second, when the voice can say it too + (`wait`). Asked for during the talk, the ask would be gone 20 s later, + and the voice would say the reminder 10 s after the talk with no look. - **Ask:** the Policy's `reminder` question. The person asked for this, so it should hold only while someone is talking to the lamp. - **Do:** look at the face, if one is in view, and ask the voice to say the diff --git a/lampOS/skills/reminders/skill.toml b/lampOS/skills/reminders/skill.toml index be925c21cc..b77f7024da 100644 --- a/lampOS/skills/reminders/skill.toml +++ b/lampOS/skills/reminders/skill.toml @@ -9,6 +9,13 @@ topic = "schedule/due" field = "pending" above = 0.5 when = "appears" # a reminder due where none was +# Due while someone talks with the lamp: asked for once the talk ends, at +# Schedule's next second, while it is still due. The voice says a reminder +# only then too, and waits 10 s for this ask, which is gone 20 s after it is +# made: asked during the talk, it would be gone by then, and the voice would +# wait its 10 s in silence and say it with no look (the reviewer's P2 on +# #1667). +wait = "1h" [[while]] # Schedule on and able to serve it topic = "schedule/due" @@ -20,6 +27,11 @@ topic = "schedule/due" field = "enabled" equals = true +[[while]] # nobody talking with the lamp, nor the lamp speaking +topic = "voice/state" +field = "state" +equals = "resting" + [ask] policy = "reminder" diff --git a/lampOS/skills/reminders/tests/due-during-a-talk-said-when-it-ends/expect.toml b/lampOS/skills/reminders/tests/due-during-a-talk-said-when-it-ends/expect.toml new file mode 100644 index 0000000000..8e00a955a6 --- /dev/null +++ b/lampOS/skills/reminders/tests/due-during-a-talk-said-when-it-ends/expect.toml @@ -0,0 +1,19 @@ +# Someone at the desk throughout, talking with the lamp from 20 s to 60.5 s +# (voice/state listening, thinking, speaking). A reminder falls due at 30 s, +# in the talk: nothing then. The talk ends at 60.5 s, and at Schedule's next +# second, 61 s, the lamp looks at them and asks the voice to say it, which +# the voice says then too, once, with the look (the reviewer's P2 on #1667: +# asked for at 30 s, the ask would be gone at 50 s, and the voice would say +# it at 71 s with no look). Acknowledged at 70 s. + +[[expect]] +at = "61s" +within = "500ms" +call = "body.gaze" +args = { look_at = "face", x = 0.0, y = -0.25, size = 0.25 } + +[[expect]] +at = "61s" +within = "500ms" +call = "say" +args = { from = "schedule" } diff --git a/lampOS/skills/reminders/tests/due-during-a-talk-said-when-it-ends/humans_faces_tracked.jsonl b/lampOS/skills/reminders/tests/due-during-a-talk-said-when-it-ends/humans_faces_tracked.jsonl new file mode 100644 index 0000000000..f05b54d414 --- /dev/null +++ b/lampOS/skills/reminders/tests/due-during-a-talk-said-when-it-ends/humans_faces_tracked.jsonl @@ -0,0 +1,92 @@ +{"topic": "humans/faces/tracked", "declaration": {"type": "lamp_msgs/Faces", "version": 1, "kind": "latest"}} +{"header": {"topic": "humans/faces/tracked", "kind": "latest", "enc": "json", "type": "lamp_msgs/Faces", "source": "eyes", "boot": "e7e5e7e5e7e5e7e5", "seq": 1, "at_us": 0, "at_unix_ms": 1792130400000}, "payload_b64": "eyJmYWNlcyI6W3siaWQiOiJhMSIsInhtaW4iOjAuMjUsInltaW4iOjAuMjUsInhtYXgiOjAuNzUsInltYXgiOjAuNSwiY29uZmlkZW5jZSI6MC45fV19"} +{"header": {"topic": "humans/faces/tracked", "kind": "latest", "enc": "json", "type": "lamp_msgs/Faces", "source": "eyes", "boot": "e7e5e7e5e7e5e7e5", "seq": 2, "at_us": 1000000, "at_unix_ms": 1792130401000}, "payload_b64": "eyJmYWNlcyI6W3siaWQiOiJhMSIsInhtaW4iOjAuMjUsInltaW4iOjAuMjUsInhtYXgiOjAuNzUsInltYXgiOjAuNSwiY29uZmlkZW5jZSI6MC45fV19"} +{"header": {"topic": "humans/faces/tracked", "kind": "latest", "enc": "json", "type": "lamp_msgs/Faces", "source": "eyes", "boot": "e7e5e7e5e7e5e7e5", "seq": 3, "at_us": 2000000, "at_unix_ms": 1792130402000}, "payload_b64": "eyJmYWNlcyI6W3siaWQiOiJhMSIsInhtaW4iOjAuMjUsInltaW4iOjAuMjUsInhtYXgiOjAuNzUsInltYXgiOjAuNSwiY29uZmlkZW5jZSI6MC45fV19"} +{"header": {"topic": "humans/faces/tracked", "kind": "latest", "enc": "json", "type": "lamp_msgs/Faces", "source": "eyes", "boot": "e7e5e7e5e7e5e7e5", "seq": 4, "at_us": 3000000, "at_unix_ms": 1792130403000}, "payload_b64": "eyJmYWNlcyI6W3siaWQiOiJhMSIsInhtaW4iOjAuMjUsInltaW4iOjAuMjUsInhtYXgiOjAuNzUsInltYXgiOjAuNSwiY29uZmlkZW5jZSI6MC45fV19"} +{"header": {"topic": "humans/faces/tracked", "kind": "latest", "enc": "json", "type": "lamp_msgs/Faces", "source": "eyes", "boot": "e7e5e7e5e7e5e7e5", "seq": 5, "at_us": 4000000, "at_unix_ms": 1792130404000}, "payload_b64": "eyJmYWNlcyI6W3siaWQiOiJhMSIsInhtaW4iOjAuMjUsInltaW4iOjAuMjUsInhtYXgiOjAuNzUsInltYXgiOjAuNSwiY29uZmlkZW5jZSI6MC45fV19"} +{"header": {"topic": "humans/faces/tracked", "kind": "latest", "enc": "json", "type": "lamp_msgs/Faces", "source": "eyes", "boot": "e7e5e7e5e7e5e7e5", "seq": 6, "at_us": 5000000, "at_unix_ms": 1792130405000}, "payload_b64": "eyJmYWNlcyI6W3siaWQiOiJhMSIsInhtaW4iOjAuMjUsInltaW4iOjAuMjUsInhtYXgiOjAuNzUsInltYXgiOjAuNSwiY29uZmlkZW5jZSI6MC45fV19"} +{"header": {"topic": "humans/faces/tracked", "kind": "latest", "enc": "json", "type": "lamp_msgs/Faces", "source": "eyes", "boot": "e7e5e7e5e7e5e7e5", "seq": 7, "at_us": 6000000, "at_unix_ms": 1792130406000}, "payload_b64": "eyJmYWNlcyI6W3siaWQiOiJhMSIsInhtaW4iOjAuMjUsInltaW4iOjAuMjUsInhtYXgiOjAuNzUsInltYXgiOjAuNSwiY29uZmlkZW5jZSI6MC45fV19"} +{"header": {"topic": "humans/faces/tracked", "kind": "latest", "enc": "json", "type": "lamp_msgs/Faces", "source": "eyes", "boot": "e7e5e7e5e7e5e7e5", "seq": 8, "at_us": 7000000, "at_unix_ms": 1792130407000}, "payload_b64": "eyJmYWNlcyI6W3siaWQiOiJhMSIsInhtaW4iOjAuMjUsInltaW4iOjAuMjUsInhtYXgiOjAuNzUsInltYXgiOjAuNSwiY29uZmlkZW5jZSI6MC45fV19"} +{"header": {"topic": "humans/faces/tracked", "kind": "latest", "enc": "json", "type": "lamp_msgs/Faces", "source": "eyes", "boot": "e7e5e7e5e7e5e7e5", "seq": 9, "at_us": 8000000, "at_unix_ms": 1792130408000}, "payload_b64": "eyJmYWNlcyI6W3siaWQiOiJhMSIsInhtaW4iOjAuMjUsInltaW4iOjAuMjUsInhtYXgiOjAuNzUsInltYXgiOjAuNSwiY29uZmlkZW5jZSI6MC45fV19"} +{"header": {"topic": "humans/faces/tracked", "kind": "latest", "enc": "json", "type": "lamp_msgs/Faces", "source": "eyes", "boot": "e7e5e7e5e7e5e7e5", "seq": 10, "at_us": 9000000, "at_unix_ms": 1792130409000}, "payload_b64": "eyJmYWNlcyI6W3siaWQiOiJhMSIsInhtaW4iOjAuMjUsInltaW4iOjAuMjUsInhtYXgiOjAuNzUsInltYXgiOjAuNSwiY29uZmlkZW5jZSI6MC45fV19"} +{"header": {"topic": "humans/faces/tracked", "kind": "latest", "enc": "json", "type": "lamp_msgs/Faces", "source": "eyes", "boot": "e7e5e7e5e7e5e7e5", "seq": 11, "at_us": 10000000, "at_unix_ms": 1792130410000}, "payload_b64": "eyJmYWNlcyI6W3siaWQiOiJhMSIsInhtaW4iOjAuMjUsInltaW4iOjAuMjUsInhtYXgiOjAuNzUsInltYXgiOjAuNSwiY29uZmlkZW5jZSI6MC45fV19"} +{"header": {"topic": "humans/faces/tracked", "kind": "latest", "enc": "json", "type": "lamp_msgs/Faces", "source": "eyes", "boot": "e7e5e7e5e7e5e7e5", "seq": 12, "at_us": 11000000, "at_unix_ms": 1792130411000}, "payload_b64": "eyJmYWNlcyI6W3siaWQiOiJhMSIsInhtaW4iOjAuMjUsInltaW4iOjAuMjUsInhtYXgiOjAuNzUsInltYXgiOjAuNSwiY29uZmlkZW5jZSI6MC45fV19"} +{"header": {"topic": "humans/faces/tracked", "kind": "latest", "enc": "json", "type": "lamp_msgs/Faces", "source": "eyes", "boot": "e7e5e7e5e7e5e7e5", "seq": 13, "at_us": 12000000, "at_unix_ms": 1792130412000}, "payload_b64": "eyJmYWNlcyI6W3siaWQiOiJhMSIsInhtaW4iOjAuMjUsInltaW4iOjAuMjUsInhtYXgiOjAuNzUsInltYXgiOjAuNSwiY29uZmlkZW5jZSI6MC45fV19"} +{"header": {"topic": "humans/faces/tracked", "kind": "latest", "enc": "json", "type": "lamp_msgs/Faces", "source": "eyes", "boot": "e7e5e7e5e7e5e7e5", "seq": 14, "at_us": 13000000, "at_unix_ms": 1792130413000}, "payload_b64": "eyJmYWNlcyI6W3siaWQiOiJhMSIsInhtaW4iOjAuMjUsInltaW4iOjAuMjUsInhtYXgiOjAuNzUsInltYXgiOjAuNSwiY29uZmlkZW5jZSI6MC45fV19"} +{"header": {"topic": "humans/faces/tracked", "kind": "latest", "enc": "json", "type": "lamp_msgs/Faces", "source": "eyes", "boot": "e7e5e7e5e7e5e7e5", "seq": 15, "at_us": 14000000, "at_unix_ms": 1792130414000}, "payload_b64": "eyJmYWNlcyI6W3siaWQiOiJhMSIsInhtaW4iOjAuMjUsInltaW4iOjAuMjUsInhtYXgiOjAuNzUsInltYXgiOjAuNSwiY29uZmlkZW5jZSI6MC45fV19"} +{"header": {"topic": "humans/faces/tracked", "kind": "latest", "enc": "json", "type": "lamp_msgs/Faces", "source": "eyes", "boot": "e7e5e7e5e7e5e7e5", "seq": 16, "at_us": 15000000, "at_unix_ms": 1792130415000}, "payload_b64": "eyJmYWNlcyI6W3siaWQiOiJhMSIsInhtaW4iOjAuMjUsInltaW4iOjAuMjUsInhtYXgiOjAuNzUsInltYXgiOjAuNSwiY29uZmlkZW5jZSI6MC45fV19"} +{"header": {"topic": "humans/faces/tracked", "kind": "latest", "enc": "json", "type": "lamp_msgs/Faces", "source": "eyes", "boot": "e7e5e7e5e7e5e7e5", "seq": 17, "at_us": 16000000, "at_unix_ms": 1792130416000}, "payload_b64": "eyJmYWNlcyI6W3siaWQiOiJhMSIsInhtaW4iOjAuMjUsInltaW4iOjAuMjUsInhtYXgiOjAuNzUsInltYXgiOjAuNSwiY29uZmlkZW5jZSI6MC45fV19"} +{"header": {"topic": "humans/faces/tracked", "kind": "latest", "enc": "json", "type": "lamp_msgs/Faces", "source": "eyes", "boot": "e7e5e7e5e7e5e7e5", "seq": 18, "at_us": 17000000, "at_unix_ms": 1792130417000}, "payload_b64": "eyJmYWNlcyI6W3siaWQiOiJhMSIsInhtaW4iOjAuMjUsInltaW4iOjAuMjUsInhtYXgiOjAuNzUsInltYXgiOjAuNSwiY29uZmlkZW5jZSI6MC45fV19"} +{"header": {"topic": "humans/faces/tracked", "kind": "latest", "enc": "json", "type": "lamp_msgs/Faces", "source": "eyes", "boot": "e7e5e7e5e7e5e7e5", "seq": 19, "at_us": 18000000, "at_unix_ms": 1792130418000}, "payload_b64": "eyJmYWNlcyI6W3siaWQiOiJhMSIsInhtaW4iOjAuMjUsInltaW4iOjAuMjUsInhtYXgiOjAuNzUsInltYXgiOjAuNSwiY29uZmlkZW5jZSI6MC45fV19"} +{"header": {"topic": "humans/faces/tracked", "kind": "latest", "enc": "json", "type": "lamp_msgs/Faces", "source": "eyes", "boot": "e7e5e7e5e7e5e7e5", "seq": 20, "at_us": 19000000, "at_unix_ms": 1792130419000}, "payload_b64": "eyJmYWNlcyI6W3siaWQiOiJhMSIsInhtaW4iOjAuMjUsInltaW4iOjAuMjUsInhtYXgiOjAuNzUsInltYXgiOjAuNSwiY29uZmlkZW5jZSI6MC45fV19"} +{"header": {"topic": "humans/faces/tracked", "kind": "latest", "enc": "json", "type": "lamp_msgs/Faces", "source": "eyes", "boot": "e7e5e7e5e7e5e7e5", "seq": 21, "at_us": 20000000, "at_unix_ms": 1792130420000}, "payload_b64": "eyJmYWNlcyI6W3siaWQiOiJhMSIsInhtaW4iOjAuMjUsInltaW4iOjAuMjUsInhtYXgiOjAuNzUsInltYXgiOjAuNSwiY29uZmlkZW5jZSI6MC45fV19"} +{"header": {"topic": "humans/faces/tracked", "kind": "latest", "enc": "json", "type": "lamp_msgs/Faces", "source": "eyes", "boot": "e7e5e7e5e7e5e7e5", "seq": 22, "at_us": 21000000, "at_unix_ms": 1792130421000}, "payload_b64": "eyJmYWNlcyI6W3siaWQiOiJhMSIsInhtaW4iOjAuMjUsInltaW4iOjAuMjUsInhtYXgiOjAuNzUsInltYXgiOjAuNSwiY29uZmlkZW5jZSI6MC45fV19"} +{"header": {"topic": "humans/faces/tracked", "kind": "latest", "enc": "json", "type": "lamp_msgs/Faces", "source": "eyes", "boot": "e7e5e7e5e7e5e7e5", "seq": 23, "at_us": 22000000, "at_unix_ms": 1792130422000}, "payload_b64": "eyJmYWNlcyI6W3siaWQiOiJhMSIsInhtaW4iOjAuMjUsInltaW4iOjAuMjUsInhtYXgiOjAuNzUsInltYXgiOjAuNSwiY29uZmlkZW5jZSI6MC45fV19"} +{"header": {"topic": "humans/faces/tracked", "kind": "latest", "enc": "json", "type": "lamp_msgs/Faces", "source": "eyes", "boot": "e7e5e7e5e7e5e7e5", "seq": 24, "at_us": 23000000, "at_unix_ms": 1792130423000}, "payload_b64": "eyJmYWNlcyI6W3siaWQiOiJhMSIsInhtaW4iOjAuMjUsInltaW4iOjAuMjUsInhtYXgiOjAuNzUsInltYXgiOjAuNSwiY29uZmlkZW5jZSI6MC45fV19"} +{"header": {"topic": "humans/faces/tracked", "kind": "latest", "enc": "json", "type": "lamp_msgs/Faces", "source": "eyes", "boot": "e7e5e7e5e7e5e7e5", "seq": 25, "at_us": 24000000, "at_unix_ms": 1792130424000}, "payload_b64": "eyJmYWNlcyI6W3siaWQiOiJhMSIsInhtaW4iOjAuMjUsInltaW4iOjAuMjUsInhtYXgiOjAuNzUsInltYXgiOjAuNSwiY29uZmlkZW5jZSI6MC45fV19"} +{"header": {"topic": "humans/faces/tracked", "kind": "latest", "enc": "json", "type": "lamp_msgs/Faces", "source": "eyes", "boot": "e7e5e7e5e7e5e7e5", "seq": 26, "at_us": 25000000, "at_unix_ms": 1792130425000}, "payload_b64": "eyJmYWNlcyI6W3siaWQiOiJhMSIsInhtaW4iOjAuMjUsInltaW4iOjAuMjUsInhtYXgiOjAuNzUsInltYXgiOjAuNSwiY29uZmlkZW5jZSI6MC45fV19"} +{"header": {"topic": "humans/faces/tracked", "kind": "latest", "enc": "json", "type": "lamp_msgs/Faces", "source": "eyes", "boot": "e7e5e7e5e7e5e7e5", "seq": 27, "at_us": 26000000, "at_unix_ms": 1792130426000}, "payload_b64": "eyJmYWNlcyI6W3siaWQiOiJhMSIsInhtaW4iOjAuMjUsInltaW4iOjAuMjUsInhtYXgiOjAuNzUsInltYXgiOjAuNSwiY29uZmlkZW5jZSI6MC45fV19"} +{"header": {"topic": "humans/faces/tracked", "kind": "latest", "enc": "json", "type": "lamp_msgs/Faces", "source": "eyes", "boot": "e7e5e7e5e7e5e7e5", "seq": 28, "at_us": 27000000, "at_unix_ms": 1792130427000}, "payload_b64": "eyJmYWNlcyI6W3siaWQiOiJhMSIsInhtaW4iOjAuMjUsInltaW4iOjAuMjUsInhtYXgiOjAuNzUsInltYXgiOjAuNSwiY29uZmlkZW5jZSI6MC45fV19"} +{"header": {"topic": "humans/faces/tracked", "kind": "latest", "enc": "json", "type": "lamp_msgs/Faces", "source": "eyes", "boot": "e7e5e7e5e7e5e7e5", "seq": 29, "at_us": 28000000, "at_unix_ms": 1792130428000}, "payload_b64": "eyJmYWNlcyI6W3siaWQiOiJhMSIsInhtaW4iOjAuMjUsInltaW4iOjAuMjUsInhtYXgiOjAuNzUsInltYXgiOjAuNSwiY29uZmlkZW5jZSI6MC45fV19"} +{"header": {"topic": "humans/faces/tracked", "kind": "latest", "enc": "json", "type": "lamp_msgs/Faces", "source": "eyes", "boot": "e7e5e7e5e7e5e7e5", "seq": 30, "at_us": 29000000, "at_unix_ms": 1792130429000}, "payload_b64": "eyJmYWNlcyI6W3siaWQiOiJhMSIsInhtaW4iOjAuMjUsInltaW4iOjAuMjUsInhtYXgiOjAuNzUsInltYXgiOjAuNSwiY29uZmlkZW5jZSI6MC45fV19"} +{"header": {"topic": "humans/faces/tracked", "kind": "latest", "enc": "json", "type": "lamp_msgs/Faces", "source": "eyes", "boot": "e7e5e7e5e7e5e7e5", "seq": 31, "at_us": 30000000, "at_unix_ms": 1792130430000}, "payload_b64": "eyJmYWNlcyI6W3siaWQiOiJhMSIsInhtaW4iOjAuMjUsInltaW4iOjAuMjUsInhtYXgiOjAuNzUsInltYXgiOjAuNSwiY29uZmlkZW5jZSI6MC45fV19"} +{"header": {"topic": "humans/faces/tracked", "kind": "latest", "enc": "json", "type": "lamp_msgs/Faces", "source": "eyes", "boot": "e7e5e7e5e7e5e7e5", "seq": 32, "at_us": 31000000, "at_unix_ms": 1792130431000}, "payload_b64": "eyJmYWNlcyI6W3siaWQiOiJhMSIsInhtaW4iOjAuMjUsInltaW4iOjAuMjUsInhtYXgiOjAuNzUsInltYXgiOjAuNSwiY29uZmlkZW5jZSI6MC45fV19"} +{"header": {"topic": "humans/faces/tracked", "kind": "latest", "enc": "json", "type": "lamp_msgs/Faces", "source": "eyes", "boot": "e7e5e7e5e7e5e7e5", "seq": 33, "at_us": 32000000, "at_unix_ms": 1792130432000}, "payload_b64": "eyJmYWNlcyI6W3siaWQiOiJhMSIsInhtaW4iOjAuMjUsInltaW4iOjAuMjUsInhtYXgiOjAuNzUsInltYXgiOjAuNSwiY29uZmlkZW5jZSI6MC45fV19"} +{"header": {"topic": "humans/faces/tracked", "kind": "latest", "enc": "json", "type": "lamp_msgs/Faces", "source": "eyes", "boot": "e7e5e7e5e7e5e7e5", "seq": 34, "at_us": 33000000, "at_unix_ms": 1792130433000}, "payload_b64": "eyJmYWNlcyI6W3siaWQiOiJhMSIsInhtaW4iOjAuMjUsInltaW4iOjAuMjUsInhtYXgiOjAuNzUsInltYXgiOjAuNSwiY29uZmlkZW5jZSI6MC45fV19"} +{"header": {"topic": "humans/faces/tracked", "kind": "latest", "enc": "json", "type": "lamp_msgs/Faces", "source": "eyes", "boot": "e7e5e7e5e7e5e7e5", "seq": 35, "at_us": 34000000, "at_unix_ms": 1792130434000}, "payload_b64": "eyJmYWNlcyI6W3siaWQiOiJhMSIsInhtaW4iOjAuMjUsInltaW4iOjAuMjUsInhtYXgiOjAuNzUsInltYXgiOjAuNSwiY29uZmlkZW5jZSI6MC45fV19"} +{"header": {"topic": "humans/faces/tracked", "kind": "latest", "enc": "json", "type": "lamp_msgs/Faces", "source": "eyes", "boot": "e7e5e7e5e7e5e7e5", "seq": 36, "at_us": 35000000, "at_unix_ms": 1792130435000}, "payload_b64": "eyJmYWNlcyI6W3siaWQiOiJhMSIsInhtaW4iOjAuMjUsInltaW4iOjAuMjUsInhtYXgiOjAuNzUsInltYXgiOjAuNSwiY29uZmlkZW5jZSI6MC45fV19"} +{"header": {"topic": "humans/faces/tracked", "kind": "latest", "enc": "json", "type": "lamp_msgs/Faces", "source": "eyes", "boot": "e7e5e7e5e7e5e7e5", "seq": 37, "at_us": 36000000, "at_unix_ms": 1792130436000}, "payload_b64": "eyJmYWNlcyI6W3siaWQiOiJhMSIsInhtaW4iOjAuMjUsInltaW4iOjAuMjUsInhtYXgiOjAuNzUsInltYXgiOjAuNSwiY29uZmlkZW5jZSI6MC45fV19"} +{"header": {"topic": "humans/faces/tracked", "kind": "latest", "enc": "json", "type": "lamp_msgs/Faces", "source": "eyes", "boot": "e7e5e7e5e7e5e7e5", "seq": 38, "at_us": 37000000, "at_unix_ms": 1792130437000}, "payload_b64": "eyJmYWNlcyI6W3siaWQiOiJhMSIsInhtaW4iOjAuMjUsInltaW4iOjAuMjUsInhtYXgiOjAuNzUsInltYXgiOjAuNSwiY29uZmlkZW5jZSI6MC45fV19"} +{"header": {"topic": "humans/faces/tracked", "kind": "latest", "enc": "json", "type": "lamp_msgs/Faces", "source": "eyes", "boot": "e7e5e7e5e7e5e7e5", "seq": 39, "at_us": 38000000, "at_unix_ms": 1792130438000}, "payload_b64": "eyJmYWNlcyI6W3siaWQiOiJhMSIsInhtaW4iOjAuMjUsInltaW4iOjAuMjUsInhtYXgiOjAuNzUsInltYXgiOjAuNSwiY29uZmlkZW5jZSI6MC45fV19"} +{"header": {"topic": "humans/faces/tracked", "kind": "latest", "enc": "json", "type": "lamp_msgs/Faces", "source": "eyes", "boot": "e7e5e7e5e7e5e7e5", "seq": 40, "at_us": 39000000, "at_unix_ms": 1792130439000}, "payload_b64": "eyJmYWNlcyI6W3siaWQiOiJhMSIsInhtaW4iOjAuMjUsInltaW4iOjAuMjUsInhtYXgiOjAuNzUsInltYXgiOjAuNSwiY29uZmlkZW5jZSI6MC45fV19"} +{"header": {"topic": "humans/faces/tracked", "kind": "latest", "enc": "json", "type": "lamp_msgs/Faces", "source": "eyes", "boot": "e7e5e7e5e7e5e7e5", "seq": 41, "at_us": 40000000, "at_unix_ms": 1792130440000}, "payload_b64": "eyJmYWNlcyI6W3siaWQiOiJhMSIsInhtaW4iOjAuMjUsInltaW4iOjAuMjUsInhtYXgiOjAuNzUsInltYXgiOjAuNSwiY29uZmlkZW5jZSI6MC45fV19"} +{"header": {"topic": "humans/faces/tracked", "kind": "latest", "enc": "json", "type": "lamp_msgs/Faces", "source": "eyes", "boot": "e7e5e7e5e7e5e7e5", "seq": 42, "at_us": 41000000, "at_unix_ms": 1792130441000}, "payload_b64": "eyJmYWNlcyI6W3siaWQiOiJhMSIsInhtaW4iOjAuMjUsInltaW4iOjAuMjUsInhtYXgiOjAuNzUsInltYXgiOjAuNSwiY29uZmlkZW5jZSI6MC45fV19"} +{"header": {"topic": "humans/faces/tracked", "kind": "latest", "enc": "json", "type": "lamp_msgs/Faces", "source": "eyes", "boot": "e7e5e7e5e7e5e7e5", "seq": 43, "at_us": 42000000, "at_unix_ms": 1792130442000}, "payload_b64": "eyJmYWNlcyI6W3siaWQiOiJhMSIsInhtaW4iOjAuMjUsInltaW4iOjAuMjUsInhtYXgiOjAuNzUsInltYXgiOjAuNSwiY29uZmlkZW5jZSI6MC45fV19"} +{"header": {"topic": "humans/faces/tracked", "kind": "latest", "enc": "json", "type": "lamp_msgs/Faces", "source": "eyes", "boot": "e7e5e7e5e7e5e7e5", "seq": 44, "at_us": 43000000, "at_unix_ms": 1792130443000}, "payload_b64": "eyJmYWNlcyI6W3siaWQiOiJhMSIsInhtaW4iOjAuMjUsInltaW4iOjAuMjUsInhtYXgiOjAuNzUsInltYXgiOjAuNSwiY29uZmlkZW5jZSI6MC45fV19"} +{"header": {"topic": "humans/faces/tracked", "kind": "latest", "enc": "json", "type": "lamp_msgs/Faces", "source": "eyes", "boot": "e7e5e7e5e7e5e7e5", "seq": 45, "at_us": 44000000, "at_unix_ms": 1792130444000}, "payload_b64": "eyJmYWNlcyI6W3siaWQiOiJhMSIsInhtaW4iOjAuMjUsInltaW4iOjAuMjUsInhtYXgiOjAuNzUsInltYXgiOjAuNSwiY29uZmlkZW5jZSI6MC45fV19"} +{"header": {"topic": "humans/faces/tracked", "kind": "latest", "enc": "json", "type": "lamp_msgs/Faces", "source": "eyes", "boot": "e7e5e7e5e7e5e7e5", "seq": 46, "at_us": 45000000, "at_unix_ms": 1792130445000}, "payload_b64": "eyJmYWNlcyI6W3siaWQiOiJhMSIsInhtaW4iOjAuMjUsInltaW4iOjAuMjUsInhtYXgiOjAuNzUsInltYXgiOjAuNSwiY29uZmlkZW5jZSI6MC45fV19"} +{"header": {"topic": "humans/faces/tracked", "kind": "latest", "enc": "json", "type": "lamp_msgs/Faces", "source": "eyes", "boot": "e7e5e7e5e7e5e7e5", "seq": 47, "at_us": 46000000, "at_unix_ms": 1792130446000}, "payload_b64": "eyJmYWNlcyI6W3siaWQiOiJhMSIsInhtaW4iOjAuMjUsInltaW4iOjAuMjUsInhtYXgiOjAuNzUsInltYXgiOjAuNSwiY29uZmlkZW5jZSI6MC45fV19"} +{"header": {"topic": "humans/faces/tracked", "kind": "latest", "enc": "json", "type": "lamp_msgs/Faces", "source": "eyes", "boot": "e7e5e7e5e7e5e7e5", "seq": 48, "at_us": 47000000, "at_unix_ms": 1792130447000}, "payload_b64": "eyJmYWNlcyI6W3siaWQiOiJhMSIsInhtaW4iOjAuMjUsInltaW4iOjAuMjUsInhtYXgiOjAuNzUsInltYXgiOjAuNSwiY29uZmlkZW5jZSI6MC45fV19"} +{"header": {"topic": "humans/faces/tracked", "kind": "latest", "enc": "json", "type": "lamp_msgs/Faces", "source": "eyes", "boot": "e7e5e7e5e7e5e7e5", "seq": 49, "at_us": 48000000, "at_unix_ms": 1792130448000}, "payload_b64": "eyJmYWNlcyI6W3siaWQiOiJhMSIsInhtaW4iOjAuMjUsInltaW4iOjAuMjUsInhtYXgiOjAuNzUsInltYXgiOjAuNSwiY29uZmlkZW5jZSI6MC45fV19"} +{"header": {"topic": "humans/faces/tracked", "kind": "latest", "enc": "json", "type": "lamp_msgs/Faces", "source": "eyes", "boot": "e7e5e7e5e7e5e7e5", "seq": 50, "at_us": 49000000, "at_unix_ms": 1792130449000}, "payload_b64": "eyJmYWNlcyI6W3siaWQiOiJhMSIsInhtaW4iOjAuMjUsInltaW4iOjAuMjUsInhtYXgiOjAuNzUsInltYXgiOjAuNSwiY29uZmlkZW5jZSI6MC45fV19"} +{"header": {"topic": "humans/faces/tracked", "kind": "latest", "enc": "json", "type": "lamp_msgs/Faces", "source": "eyes", "boot": "e7e5e7e5e7e5e7e5", "seq": 51, "at_us": 50000000, "at_unix_ms": 1792130450000}, "payload_b64": "eyJmYWNlcyI6W3siaWQiOiJhMSIsInhtaW4iOjAuMjUsInltaW4iOjAuMjUsInhtYXgiOjAuNzUsInltYXgiOjAuNSwiY29uZmlkZW5jZSI6MC45fV19"} +{"header": {"topic": "humans/faces/tracked", "kind": "latest", "enc": "json", "type": "lamp_msgs/Faces", "source": "eyes", "boot": "e7e5e7e5e7e5e7e5", "seq": 52, "at_us": 51000000, "at_unix_ms": 1792130451000}, "payload_b64": "eyJmYWNlcyI6W3siaWQiOiJhMSIsInhtaW4iOjAuMjUsInltaW4iOjAuMjUsInhtYXgiOjAuNzUsInltYXgiOjAuNSwiY29uZmlkZW5jZSI6MC45fV19"} +{"header": {"topic": "humans/faces/tracked", "kind": "latest", "enc": "json", "type": "lamp_msgs/Faces", "source": "eyes", "boot": "e7e5e7e5e7e5e7e5", "seq": 53, "at_us": 52000000, "at_unix_ms": 1792130452000}, "payload_b64": "eyJmYWNlcyI6W3siaWQiOiJhMSIsInhtaW4iOjAuMjUsInltaW4iOjAuMjUsInhtYXgiOjAuNzUsInltYXgiOjAuNSwiY29uZmlkZW5jZSI6MC45fV19"} +{"header": {"topic": "humans/faces/tracked", "kind": "latest", "enc": "json", "type": "lamp_msgs/Faces", "source": "eyes", "boot": "e7e5e7e5e7e5e7e5", "seq": 54, "at_us": 53000000, "at_unix_ms": 1792130453000}, "payload_b64": "eyJmYWNlcyI6W3siaWQiOiJhMSIsInhtaW4iOjAuMjUsInltaW4iOjAuMjUsInhtYXgiOjAuNzUsInltYXgiOjAuNSwiY29uZmlkZW5jZSI6MC45fV19"} +{"header": {"topic": "humans/faces/tracked", "kind": "latest", "enc": "json", "type": "lamp_msgs/Faces", "source": "eyes", "boot": "e7e5e7e5e7e5e7e5", "seq": 55, "at_us": 54000000, "at_unix_ms": 1792130454000}, "payload_b64": "eyJmYWNlcyI6W3siaWQiOiJhMSIsInhtaW4iOjAuMjUsInltaW4iOjAuMjUsInhtYXgiOjAuNzUsInltYXgiOjAuNSwiY29uZmlkZW5jZSI6MC45fV19"} +{"header": {"topic": "humans/faces/tracked", "kind": "latest", "enc": "json", "type": "lamp_msgs/Faces", "source": "eyes", "boot": "e7e5e7e5e7e5e7e5", "seq": 56, "at_us": 55000000, "at_unix_ms": 1792130455000}, "payload_b64": "eyJmYWNlcyI6W3siaWQiOiJhMSIsInhtaW4iOjAuMjUsInltaW4iOjAuMjUsInhtYXgiOjAuNzUsInltYXgiOjAuNSwiY29uZmlkZW5jZSI6MC45fV19"} +{"header": {"topic": "humans/faces/tracked", "kind": "latest", "enc": "json", "type": "lamp_msgs/Faces", "source": "eyes", "boot": "e7e5e7e5e7e5e7e5", "seq": 57, "at_us": 56000000, "at_unix_ms": 1792130456000}, "payload_b64": "eyJmYWNlcyI6W3siaWQiOiJhMSIsInhtaW4iOjAuMjUsInltaW4iOjAuMjUsInhtYXgiOjAuNzUsInltYXgiOjAuNSwiY29uZmlkZW5jZSI6MC45fV19"} +{"header": {"topic": "humans/faces/tracked", "kind": "latest", "enc": "json", "type": "lamp_msgs/Faces", "source": "eyes", "boot": "e7e5e7e5e7e5e7e5", "seq": 58, "at_us": 57000000, "at_unix_ms": 1792130457000}, "payload_b64": "eyJmYWNlcyI6W3siaWQiOiJhMSIsInhtaW4iOjAuMjUsInltaW4iOjAuMjUsInhtYXgiOjAuNzUsInltYXgiOjAuNSwiY29uZmlkZW5jZSI6MC45fV19"} +{"header": {"topic": "humans/faces/tracked", "kind": "latest", "enc": "json", "type": "lamp_msgs/Faces", "source": "eyes", "boot": "e7e5e7e5e7e5e7e5", "seq": 59, "at_us": 58000000, "at_unix_ms": 1792130458000}, "payload_b64": "eyJmYWNlcyI6W3siaWQiOiJhMSIsInhtaW4iOjAuMjUsInltaW4iOjAuMjUsInhtYXgiOjAuNzUsInltYXgiOjAuNSwiY29uZmlkZW5jZSI6MC45fV19"} +{"header": {"topic": "humans/faces/tracked", "kind": "latest", "enc": "json", "type": "lamp_msgs/Faces", "source": "eyes", "boot": "e7e5e7e5e7e5e7e5", "seq": 60, "at_us": 59000000, "at_unix_ms": 1792130459000}, "payload_b64": "eyJmYWNlcyI6W3siaWQiOiJhMSIsInhtaW4iOjAuMjUsInltaW4iOjAuMjUsInhtYXgiOjAuNzUsInltYXgiOjAuNSwiY29uZmlkZW5jZSI6MC45fV19"} +{"header": {"topic": "humans/faces/tracked", "kind": "latest", "enc": "json", "type": "lamp_msgs/Faces", "source": "eyes", "boot": "e7e5e7e5e7e5e7e5", "seq": 61, "at_us": 60000000, "at_unix_ms": 1792130460000}, "payload_b64": "eyJmYWNlcyI6W3siaWQiOiJhMSIsInhtaW4iOjAuMjUsInltaW4iOjAuMjUsInhtYXgiOjAuNzUsInltYXgiOjAuNSwiY29uZmlkZW5jZSI6MC45fV19"} +{"header": {"topic": "humans/faces/tracked", "kind": "latest", "enc": "json", "type": "lamp_msgs/Faces", "source": "eyes", "boot": "e7e5e7e5e7e5e7e5", "seq": 62, "at_us": 61000000, "at_unix_ms": 1792130461000}, "payload_b64": "eyJmYWNlcyI6W3siaWQiOiJhMSIsInhtaW4iOjAuMjUsInltaW4iOjAuMjUsInhtYXgiOjAuNzUsInltYXgiOjAuNSwiY29uZmlkZW5jZSI6MC45fV19"} +{"header": {"topic": "humans/faces/tracked", "kind": "latest", "enc": "json", "type": "lamp_msgs/Faces", "source": "eyes", "boot": "e7e5e7e5e7e5e7e5", "seq": 63, "at_us": 62000000, "at_unix_ms": 1792130462000}, "payload_b64": "eyJmYWNlcyI6W3siaWQiOiJhMSIsInhtaW4iOjAuMjUsInltaW4iOjAuMjUsInhtYXgiOjAuNzUsInltYXgiOjAuNSwiY29uZmlkZW5jZSI6MC45fV19"} +{"header": {"topic": "humans/faces/tracked", "kind": "latest", "enc": "json", "type": "lamp_msgs/Faces", "source": "eyes", "boot": "e7e5e7e5e7e5e7e5", "seq": 64, "at_us": 63000000, "at_unix_ms": 1792130463000}, "payload_b64": "eyJmYWNlcyI6W3siaWQiOiJhMSIsInhtaW4iOjAuMjUsInltaW4iOjAuMjUsInhtYXgiOjAuNzUsInltYXgiOjAuNSwiY29uZmlkZW5jZSI6MC45fV19"} +{"header": {"topic": "humans/faces/tracked", "kind": "latest", "enc": "json", "type": "lamp_msgs/Faces", "source": "eyes", "boot": "e7e5e7e5e7e5e7e5", "seq": 65, "at_us": 64000000, "at_unix_ms": 1792130464000}, "payload_b64": "eyJmYWNlcyI6W3siaWQiOiJhMSIsInhtaW4iOjAuMjUsInltaW4iOjAuMjUsInhtYXgiOjAuNzUsInltYXgiOjAuNSwiY29uZmlkZW5jZSI6MC45fV19"} +{"header": {"topic": "humans/faces/tracked", "kind": "latest", "enc": "json", "type": "lamp_msgs/Faces", "source": "eyes", "boot": "e7e5e7e5e7e5e7e5", "seq": 66, "at_us": 65000000, "at_unix_ms": 1792130465000}, "payload_b64": "eyJmYWNlcyI6W3siaWQiOiJhMSIsInhtaW4iOjAuMjUsInltaW4iOjAuMjUsInhtYXgiOjAuNzUsInltYXgiOjAuNSwiY29uZmlkZW5jZSI6MC45fV19"} +{"header": {"topic": "humans/faces/tracked", "kind": "latest", "enc": "json", "type": "lamp_msgs/Faces", "source": "eyes", "boot": "e7e5e7e5e7e5e7e5", "seq": 67, "at_us": 66000000, "at_unix_ms": 1792130466000}, "payload_b64": "eyJmYWNlcyI6W3siaWQiOiJhMSIsInhtaW4iOjAuMjUsInltaW4iOjAuMjUsInhtYXgiOjAuNzUsInltYXgiOjAuNSwiY29uZmlkZW5jZSI6MC45fV19"} +{"header": {"topic": "humans/faces/tracked", "kind": "latest", "enc": "json", "type": "lamp_msgs/Faces", "source": "eyes", "boot": "e7e5e7e5e7e5e7e5", "seq": 68, "at_us": 67000000, "at_unix_ms": 1792130467000}, "payload_b64": "eyJmYWNlcyI6W3siaWQiOiJhMSIsInhtaW4iOjAuMjUsInltaW4iOjAuMjUsInhtYXgiOjAuNzUsInltYXgiOjAuNSwiY29uZmlkZW5jZSI6MC45fV19"} +{"header": {"topic": "humans/faces/tracked", "kind": "latest", "enc": "json", "type": "lamp_msgs/Faces", "source": "eyes", "boot": "e7e5e7e5e7e5e7e5", "seq": 69, "at_us": 68000000, "at_unix_ms": 1792130468000}, "payload_b64": "eyJmYWNlcyI6W3siaWQiOiJhMSIsInhtaW4iOjAuMjUsInltaW4iOjAuMjUsInhtYXgiOjAuNzUsInltYXgiOjAuNSwiY29uZmlkZW5jZSI6MC45fV19"} +{"header": {"topic": "humans/faces/tracked", "kind": "latest", "enc": "json", "type": "lamp_msgs/Faces", "source": "eyes", "boot": "e7e5e7e5e7e5e7e5", "seq": 70, "at_us": 69000000, "at_unix_ms": 1792130469000}, "payload_b64": "eyJmYWNlcyI6W3siaWQiOiJhMSIsInhtaW4iOjAuMjUsInltaW4iOjAuMjUsInhtYXgiOjAuNzUsInltYXgiOjAuNSwiY29uZmlkZW5jZSI6MC45fV19"} +{"header": {"topic": "humans/faces/tracked", "kind": "latest", "enc": "json", "type": "lamp_msgs/Faces", "source": "eyes", "boot": "e7e5e7e5e7e5e7e5", "seq": 71, "at_us": 70000000, "at_unix_ms": 1792130470000}, "payload_b64": "eyJmYWNlcyI6W3siaWQiOiJhMSIsInhtaW4iOjAuMjUsInltaW4iOjAuMjUsInhtYXgiOjAuNzUsInltYXgiOjAuNSwiY29uZmlkZW5jZSI6MC45fV19"} +{"header": {"topic": "humans/faces/tracked", "kind": "latest", "enc": "json", "type": "lamp_msgs/Faces", "source": "eyes", "boot": "e7e5e7e5e7e5e7e5", "seq": 72, "at_us": 71000000, "at_unix_ms": 1792130471000}, "payload_b64": "eyJmYWNlcyI6W3siaWQiOiJhMSIsInhtaW4iOjAuMjUsInltaW4iOjAuMjUsInhtYXgiOjAuNzUsInltYXgiOjAuNSwiY29uZmlkZW5jZSI6MC45fV19"} +{"header": {"topic": "humans/faces/tracked", "kind": "latest", "enc": "json", "type": "lamp_msgs/Faces", "source": "eyes", "boot": "e7e5e7e5e7e5e7e5", "seq": 73, "at_us": 72000000, "at_unix_ms": 1792130472000}, "payload_b64": "eyJmYWNlcyI6W3siaWQiOiJhMSIsInhtaW4iOjAuMjUsInltaW4iOjAuMjUsInhtYXgiOjAuNzUsInltYXgiOjAuNSwiY29uZmlkZW5jZSI6MC45fV19"} +{"header": {"topic": "humans/faces/tracked", "kind": "latest", "enc": "json", "type": "lamp_msgs/Faces", "source": "eyes", "boot": "e7e5e7e5e7e5e7e5", "seq": 74, "at_us": 73000000, "at_unix_ms": 1792130473000}, "payload_b64": "eyJmYWNlcyI6W3siaWQiOiJhMSIsInhtaW4iOjAuMjUsInltaW4iOjAuMjUsInhtYXgiOjAuNzUsInltYXgiOjAuNSwiY29uZmlkZW5jZSI6MC45fV19"} +{"header": {"topic": "humans/faces/tracked", "kind": "latest", "enc": "json", "type": "lamp_msgs/Faces", "source": "eyes", "boot": "e7e5e7e5e7e5e7e5", "seq": 75, "at_us": 74000000, "at_unix_ms": 1792130474000}, "payload_b64": "eyJmYWNlcyI6W3siaWQiOiJhMSIsInhtaW4iOjAuMjUsInltaW4iOjAuMjUsInhtYXgiOjAuNzUsInltYXgiOjAuNSwiY29uZmlkZW5jZSI6MC45fV19"} +{"header": {"topic": "humans/faces/tracked", "kind": "latest", "enc": "json", "type": "lamp_msgs/Faces", "source": "eyes", "boot": "e7e5e7e5e7e5e7e5", "seq": 76, "at_us": 75000000, "at_unix_ms": 1792130475000}, "payload_b64": "eyJmYWNlcyI6W3siaWQiOiJhMSIsInhtaW4iOjAuMjUsInltaW4iOjAuMjUsInhtYXgiOjAuNzUsInltYXgiOjAuNSwiY29uZmlkZW5jZSI6MC45fV19"} +{"header": {"topic": "humans/faces/tracked", "kind": "latest", "enc": "json", "type": "lamp_msgs/Faces", "source": "eyes", "boot": "e7e5e7e5e7e5e7e5", "seq": 77, "at_us": 76000000, "at_unix_ms": 1792130476000}, "payload_b64": "eyJmYWNlcyI6W3siaWQiOiJhMSIsInhtaW4iOjAuMjUsInltaW4iOjAuMjUsInhtYXgiOjAuNzUsInltYXgiOjAuNSwiY29uZmlkZW5jZSI6MC45fV19"} +{"header": {"topic": "humans/faces/tracked", "kind": "latest", "enc": "json", "type": "lamp_msgs/Faces", "source": "eyes", "boot": "e7e5e7e5e7e5e7e5", "seq": 78, "at_us": 77000000, "at_unix_ms": 1792130477000}, "payload_b64": "eyJmYWNlcyI6W3siaWQiOiJhMSIsInhtaW4iOjAuMjUsInltaW4iOjAuMjUsInhtYXgiOjAuNzUsInltYXgiOjAuNSwiY29uZmlkZW5jZSI6MC45fV19"} +{"header": {"topic": "humans/faces/tracked", "kind": "latest", "enc": "json", "type": "lamp_msgs/Faces", "source": "eyes", "boot": "e7e5e7e5e7e5e7e5", "seq": 79, "at_us": 78000000, "at_unix_ms": 1792130478000}, "payload_b64": "eyJmYWNlcyI6W3siaWQiOiJhMSIsInhtaW4iOjAuMjUsInltaW4iOjAuMjUsInhtYXgiOjAuNzUsInltYXgiOjAuNSwiY29uZmlkZW5jZSI6MC45fV19"} +{"header": {"topic": "humans/faces/tracked", "kind": "latest", "enc": "json", "type": "lamp_msgs/Faces", "source": "eyes", "boot": "e7e5e7e5e7e5e7e5", "seq": 80, "at_us": 79000000, "at_unix_ms": 1792130479000}, "payload_b64": "eyJmYWNlcyI6W3siaWQiOiJhMSIsInhtaW4iOjAuMjUsInltaW4iOjAuMjUsInhtYXgiOjAuNzUsInltYXgiOjAuNSwiY29uZmlkZW5jZSI6MC45fV19"} +{"header": {"topic": "humans/faces/tracked", "kind": "latest", "enc": "json", "type": "lamp_msgs/Faces", "source": "eyes", "boot": "e7e5e7e5e7e5e7e5", "seq": 81, "at_us": 80000000, "at_unix_ms": 1792130480000}, "payload_b64": "eyJmYWNlcyI6W3siaWQiOiJhMSIsInhtaW4iOjAuMjUsInltaW4iOjAuMjUsInhtYXgiOjAuNzUsInltYXgiOjAuNSwiY29uZmlkZW5jZSI6MC45fV19"} +{"header": {"topic": "humans/faces/tracked", "kind": "latest", "enc": "json", "type": "lamp_msgs/Faces", "source": "eyes", "boot": "e7e5e7e5e7e5e7e5", "seq": 82, "at_us": 81000000, "at_unix_ms": 1792130481000}, "payload_b64": "eyJmYWNlcyI6W3siaWQiOiJhMSIsInhtaW4iOjAuMjUsInltaW4iOjAuMjUsInhtYXgiOjAuNzUsInltYXgiOjAuNSwiY29uZmlkZW5jZSI6MC45fV19"} +{"header": {"topic": "humans/faces/tracked", "kind": "latest", "enc": "json", "type": "lamp_msgs/Faces", "source": "eyes", "boot": "e7e5e7e5e7e5e7e5", "seq": 83, "at_us": 82000000, "at_unix_ms": 1792130482000}, "payload_b64": "eyJmYWNlcyI6W3siaWQiOiJhMSIsInhtaW4iOjAuMjUsInltaW4iOjAuMjUsInhtYXgiOjAuNzUsInltYXgiOjAuNSwiY29uZmlkZW5jZSI6MC45fV19"} +{"header": {"topic": "humans/faces/tracked", "kind": "latest", "enc": "json", "type": "lamp_msgs/Faces", "source": "eyes", "boot": "e7e5e7e5e7e5e7e5", "seq": 84, "at_us": 83000000, "at_unix_ms": 1792130483000}, "payload_b64": "eyJmYWNlcyI6W3siaWQiOiJhMSIsInhtaW4iOjAuMjUsInltaW4iOjAuMjUsInhtYXgiOjAuNzUsInltYXgiOjAuNSwiY29uZmlkZW5jZSI6MC45fV19"} +{"header": {"topic": "humans/faces/tracked", "kind": "latest", "enc": "json", "type": "lamp_msgs/Faces", "source": "eyes", "boot": "e7e5e7e5e7e5e7e5", "seq": 85, "at_us": 84000000, "at_unix_ms": 1792130484000}, "payload_b64": "eyJmYWNlcyI6W3siaWQiOiJhMSIsInhtaW4iOjAuMjUsInltaW4iOjAuMjUsInhtYXgiOjAuNzUsInltYXgiOjAuNSwiY29uZmlkZW5jZSI6MC45fV19"} +{"header": {"topic": "humans/faces/tracked", "kind": "latest", "enc": "json", "type": "lamp_msgs/Faces", "source": "eyes", "boot": "e7e5e7e5e7e5e7e5", "seq": 86, "at_us": 85000000, "at_unix_ms": 1792130485000}, "payload_b64": "eyJmYWNlcyI6W3siaWQiOiJhMSIsInhtaW4iOjAuMjUsInltaW4iOjAuMjUsInhtYXgiOjAuNzUsInltYXgiOjAuNSwiY29uZmlkZW5jZSI6MC45fV19"} +{"header": {"topic": "humans/faces/tracked", "kind": "latest", "enc": "json", "type": "lamp_msgs/Faces", "source": "eyes", "boot": "e7e5e7e5e7e5e7e5", "seq": 87, "at_us": 86000000, "at_unix_ms": 1792130486000}, "payload_b64": "eyJmYWNlcyI6W3siaWQiOiJhMSIsInhtaW4iOjAuMjUsInltaW4iOjAuMjUsInhtYXgiOjAuNzUsInltYXgiOjAuNSwiY29uZmlkZW5jZSI6MC45fV19"} +{"header": {"topic": "humans/faces/tracked", "kind": "latest", "enc": "json", "type": "lamp_msgs/Faces", "source": "eyes", "boot": "e7e5e7e5e7e5e7e5", "seq": 88, "at_us": 87000000, "at_unix_ms": 1792130487000}, "payload_b64": "eyJmYWNlcyI6W3siaWQiOiJhMSIsInhtaW4iOjAuMjUsInltaW4iOjAuMjUsInhtYXgiOjAuNzUsInltYXgiOjAuNSwiY29uZmlkZW5jZSI6MC45fV19"} +{"header": {"topic": "humans/faces/tracked", "kind": "latest", "enc": "json", "type": "lamp_msgs/Faces", "source": "eyes", "boot": "e7e5e7e5e7e5e7e5", "seq": 89, "at_us": 88000000, "at_unix_ms": 1792130488000}, "payload_b64": "eyJmYWNlcyI6W3siaWQiOiJhMSIsInhtaW4iOjAuMjUsInltaW4iOjAuMjUsInhtYXgiOjAuNzUsInltYXgiOjAuNSwiY29uZmlkZW5jZSI6MC45fV19"} +{"header": {"topic": "humans/faces/tracked", "kind": "latest", "enc": "json", "type": "lamp_msgs/Faces", "source": "eyes", "boot": "e7e5e7e5e7e5e7e5", "seq": 90, "at_us": 89000000, "at_unix_ms": 1792130489000}, "payload_b64": "eyJmYWNlcyI6W3siaWQiOiJhMSIsInhtaW4iOjAuMjUsInltaW4iOjAuMjUsInhtYXgiOjAuNzUsInltYXgiOjAuNSwiY29uZmlkZW5jZSI6MC45fV19"} +{"header": {"topic": "humans/faces/tracked", "kind": "latest", "enc": "json", "type": "lamp_msgs/Faces", "source": "eyes", "boot": "e7e5e7e5e7e5e7e5", "seq": 91, "at_us": 90000000, "at_unix_ms": 1792130490000}, "payload_b64": "eyJmYWNlcyI6W3siaWQiOiJhMSIsInhtaW4iOjAuMjUsInltaW4iOjAuMjUsInhtYXgiOjAuNzUsInltYXgiOjAuNSwiY29uZmlkZW5jZSI6MC45fV19"} diff --git a/lampOS/skills/reminders/tests/due-during-a-talk-said-when-it-ends/schedule_due.jsonl b/lampOS/skills/reminders/tests/due-during-a-talk-said-when-it-ends/schedule_due.jsonl new file mode 100644 index 0000000000..adc5bbba1f --- /dev/null +++ b/lampOS/skills/reminders/tests/due-during-a-talk-said-when-it-ends/schedule_due.jsonl @@ -0,0 +1,92 @@ +{"topic": "schedule/due", "declaration": {"type": "lamp_msgs/ScheduleDue", "version": 1, "kind": "latest"}} +{"header": {"topic": "schedule/due", "kind": "latest", "enc": "json", "type": "lamp_msgs/ScheduleDue", "source": "schedule", "boot": "5c4ed01e5c4ed01e", "seq": 1, "at_us": 0, "at_unix_ms": 1792130400000}, "payload_b64": "eyJzdGFtcCI6eyJjbG9ja19pZCI6InNjaGVkdWxlIiwiYXRfdXMiOjAsInNlcXVlbmNlIjoxfSwic3RvcmVfaWQiOiIwMTIzNDU2Nzg5YWJjZGVmMDEyMzQ1Njc4OWFiY2RlZiIsInJldmlzaW9uIjozLCJlbmFibGVkIjp0cnVlLCJyZWFkeSI6dHJ1ZSwicGVuZGluZyI6MCwid2FpdGluZ19mb3JfY2xvY2siOjB9"} +{"header": {"topic": "schedule/due", "kind": "latest", "enc": "json", "type": "lamp_msgs/ScheduleDue", "source": "schedule", "boot": "5c4ed01e5c4ed01e", "seq": 2, "at_us": 1000000, "at_unix_ms": 1792130401000}, "payload_b64": "eyJzdGFtcCI6eyJjbG9ja19pZCI6InNjaGVkdWxlIiwiYXRfdXMiOjEwMDAwMDAsInNlcXVlbmNlIjoyfSwic3RvcmVfaWQiOiIwMTIzNDU2Nzg5YWJjZGVmMDEyMzQ1Njc4OWFiY2RlZiIsInJldmlzaW9uIjozLCJlbmFibGVkIjp0cnVlLCJyZWFkeSI6dHJ1ZSwicGVuZGluZyI6MCwid2FpdGluZ19mb3JfY2xvY2siOjB9"} +{"header": {"topic": "schedule/due", "kind": "latest", "enc": "json", "type": "lamp_msgs/ScheduleDue", "source": "schedule", "boot": "5c4ed01e5c4ed01e", "seq": 3, "at_us": 2000000, "at_unix_ms": 1792130402000}, "payload_b64": "eyJzdGFtcCI6eyJjbG9ja19pZCI6InNjaGVkdWxlIiwiYXRfdXMiOjIwMDAwMDAsInNlcXVlbmNlIjozfSwic3RvcmVfaWQiOiIwMTIzNDU2Nzg5YWJjZGVmMDEyMzQ1Njc4OWFiY2RlZiIsInJldmlzaW9uIjozLCJlbmFibGVkIjp0cnVlLCJyZWFkeSI6dHJ1ZSwicGVuZGluZyI6MCwid2FpdGluZ19mb3JfY2xvY2siOjB9"} +{"header": {"topic": "schedule/due", "kind": "latest", "enc": "json", "type": "lamp_msgs/ScheduleDue", "source": "schedule", "boot": "5c4ed01e5c4ed01e", "seq": 4, "at_us": 3000000, "at_unix_ms": 1792130403000}, "payload_b64": "eyJzdGFtcCI6eyJjbG9ja19pZCI6InNjaGVkdWxlIiwiYXRfdXMiOjMwMDAwMDAsInNlcXVlbmNlIjo0fSwic3RvcmVfaWQiOiIwMTIzNDU2Nzg5YWJjZGVmMDEyMzQ1Njc4OWFiY2RlZiIsInJldmlzaW9uIjozLCJlbmFibGVkIjp0cnVlLCJyZWFkeSI6dHJ1ZSwicGVuZGluZyI6MCwid2FpdGluZ19mb3JfY2xvY2siOjB9"} +{"header": {"topic": "schedule/due", "kind": "latest", "enc": "json", "type": "lamp_msgs/ScheduleDue", "source": "schedule", "boot": "5c4ed01e5c4ed01e", "seq": 5, "at_us": 4000000, "at_unix_ms": 1792130404000}, "payload_b64": "eyJzdGFtcCI6eyJjbG9ja19pZCI6InNjaGVkdWxlIiwiYXRfdXMiOjQwMDAwMDAsInNlcXVlbmNlIjo1fSwic3RvcmVfaWQiOiIwMTIzNDU2Nzg5YWJjZGVmMDEyMzQ1Njc4OWFiY2RlZiIsInJldmlzaW9uIjozLCJlbmFibGVkIjp0cnVlLCJyZWFkeSI6dHJ1ZSwicGVuZGluZyI6MCwid2FpdGluZ19mb3JfY2xvY2siOjB9"} +{"header": {"topic": "schedule/due", "kind": "latest", "enc": "json", "type": "lamp_msgs/ScheduleDue", "source": "schedule", "boot": "5c4ed01e5c4ed01e", "seq": 6, "at_us": 5000000, "at_unix_ms": 1792130405000}, "payload_b64": "eyJzdGFtcCI6eyJjbG9ja19pZCI6InNjaGVkdWxlIiwiYXRfdXMiOjUwMDAwMDAsInNlcXVlbmNlIjo2fSwic3RvcmVfaWQiOiIwMTIzNDU2Nzg5YWJjZGVmMDEyMzQ1Njc4OWFiY2RlZiIsInJldmlzaW9uIjozLCJlbmFibGVkIjp0cnVlLCJyZWFkeSI6dHJ1ZSwicGVuZGluZyI6MCwid2FpdGluZ19mb3JfY2xvY2siOjB9"} +{"header": {"topic": "schedule/due", "kind": "latest", "enc": "json", "type": "lamp_msgs/ScheduleDue", "source": "schedule", "boot": "5c4ed01e5c4ed01e", "seq": 7, "at_us": 6000000, "at_unix_ms": 1792130406000}, "payload_b64": "eyJzdGFtcCI6eyJjbG9ja19pZCI6InNjaGVkdWxlIiwiYXRfdXMiOjYwMDAwMDAsInNlcXVlbmNlIjo3fSwic3RvcmVfaWQiOiIwMTIzNDU2Nzg5YWJjZGVmMDEyMzQ1Njc4OWFiY2RlZiIsInJldmlzaW9uIjozLCJlbmFibGVkIjp0cnVlLCJyZWFkeSI6dHJ1ZSwicGVuZGluZyI6MCwid2FpdGluZ19mb3JfY2xvY2siOjB9"} +{"header": {"topic": "schedule/due", "kind": "latest", "enc": "json", "type": "lamp_msgs/ScheduleDue", "source": "schedule", "boot": "5c4ed01e5c4ed01e", "seq": 8, "at_us": 7000000, "at_unix_ms": 1792130407000}, "payload_b64": "eyJzdGFtcCI6eyJjbG9ja19pZCI6InNjaGVkdWxlIiwiYXRfdXMiOjcwMDAwMDAsInNlcXVlbmNlIjo4fSwic3RvcmVfaWQiOiIwMTIzNDU2Nzg5YWJjZGVmMDEyMzQ1Njc4OWFiY2RlZiIsInJldmlzaW9uIjozLCJlbmFibGVkIjp0cnVlLCJyZWFkeSI6dHJ1ZSwicGVuZGluZyI6MCwid2FpdGluZ19mb3JfY2xvY2siOjB9"} +{"header": {"topic": "schedule/due", "kind": "latest", "enc": "json", "type": "lamp_msgs/ScheduleDue", "source": "schedule", "boot": "5c4ed01e5c4ed01e", "seq": 9, "at_us": 8000000, "at_unix_ms": 1792130408000}, "payload_b64": "eyJzdGFtcCI6eyJjbG9ja19pZCI6InNjaGVkdWxlIiwiYXRfdXMiOjgwMDAwMDAsInNlcXVlbmNlIjo5fSwic3RvcmVfaWQiOiIwMTIzNDU2Nzg5YWJjZGVmMDEyMzQ1Njc4OWFiY2RlZiIsInJldmlzaW9uIjozLCJlbmFibGVkIjp0cnVlLCJyZWFkeSI6dHJ1ZSwicGVuZGluZyI6MCwid2FpdGluZ19mb3JfY2xvY2siOjB9"} +{"header": {"topic": "schedule/due", "kind": "latest", "enc": "json", "type": "lamp_msgs/ScheduleDue", "source": "schedule", "boot": "5c4ed01e5c4ed01e", "seq": 10, "at_us": 9000000, "at_unix_ms": 1792130409000}, "payload_b64": "eyJzdGFtcCI6eyJjbG9ja19pZCI6InNjaGVkdWxlIiwiYXRfdXMiOjkwMDAwMDAsInNlcXVlbmNlIjoxMH0sInN0b3JlX2lkIjoiMDEyMzQ1Njc4OWFiY2RlZjAxMjM0NTY3ODlhYmNkZWYiLCJyZXZpc2lvbiI6MywiZW5hYmxlZCI6dHJ1ZSwicmVhZHkiOnRydWUsInBlbmRpbmciOjAsIndhaXRpbmdfZm9yX2Nsb2NrIjowfQ=="} +{"header": {"topic": "schedule/due", "kind": "latest", "enc": "json", "type": "lamp_msgs/ScheduleDue", "source": "schedule", "boot": "5c4ed01e5c4ed01e", "seq": 11, "at_us": 10000000, "at_unix_ms": 1792130410000}, "payload_b64": "eyJzdGFtcCI6eyJjbG9ja19pZCI6InNjaGVkdWxlIiwiYXRfdXMiOjEwMDAwMDAwLCJzZXF1ZW5jZSI6MTF9LCJzdG9yZV9pZCI6IjAxMjM0NTY3ODlhYmNkZWYwMTIzNDU2Nzg5YWJjZGVmIiwicmV2aXNpb24iOjMsImVuYWJsZWQiOnRydWUsInJlYWR5Ijp0cnVlLCJwZW5kaW5nIjowLCJ3YWl0aW5nX2Zvcl9jbG9jayI6MH0="} +{"header": {"topic": "schedule/due", "kind": "latest", "enc": "json", "type": "lamp_msgs/ScheduleDue", "source": "schedule", "boot": "5c4ed01e5c4ed01e", "seq": 12, "at_us": 11000000, "at_unix_ms": 1792130411000}, "payload_b64": "eyJzdGFtcCI6eyJjbG9ja19pZCI6InNjaGVkdWxlIiwiYXRfdXMiOjExMDAwMDAwLCJzZXF1ZW5jZSI6MTJ9LCJzdG9yZV9pZCI6IjAxMjM0NTY3ODlhYmNkZWYwMTIzNDU2Nzg5YWJjZGVmIiwicmV2aXNpb24iOjMsImVuYWJsZWQiOnRydWUsInJlYWR5Ijp0cnVlLCJwZW5kaW5nIjowLCJ3YWl0aW5nX2Zvcl9jbG9jayI6MH0="} +{"header": {"topic": "schedule/due", "kind": "latest", "enc": "json", "type": "lamp_msgs/ScheduleDue", "source": "schedule", "boot": "5c4ed01e5c4ed01e", "seq": 13, "at_us": 12000000, "at_unix_ms": 1792130412000}, "payload_b64": "eyJzdGFtcCI6eyJjbG9ja19pZCI6InNjaGVkdWxlIiwiYXRfdXMiOjEyMDAwMDAwLCJzZXF1ZW5jZSI6MTN9LCJzdG9yZV9pZCI6IjAxMjM0NTY3ODlhYmNkZWYwMTIzNDU2Nzg5YWJjZGVmIiwicmV2aXNpb24iOjMsImVuYWJsZWQiOnRydWUsInJlYWR5Ijp0cnVlLCJwZW5kaW5nIjowLCJ3YWl0aW5nX2Zvcl9jbG9jayI6MH0="} +{"header": {"topic": "schedule/due", "kind": "latest", "enc": "json", "type": "lamp_msgs/ScheduleDue", "source": "schedule", "boot": "5c4ed01e5c4ed01e", "seq": 14, "at_us": 13000000, "at_unix_ms": 1792130413000}, "payload_b64": "eyJzdGFtcCI6eyJjbG9ja19pZCI6InNjaGVkdWxlIiwiYXRfdXMiOjEzMDAwMDAwLCJzZXF1ZW5jZSI6MTR9LCJzdG9yZV9pZCI6IjAxMjM0NTY3ODlhYmNkZWYwMTIzNDU2Nzg5YWJjZGVmIiwicmV2aXNpb24iOjMsImVuYWJsZWQiOnRydWUsInJlYWR5Ijp0cnVlLCJwZW5kaW5nIjowLCJ3YWl0aW5nX2Zvcl9jbG9jayI6MH0="} +{"header": {"topic": "schedule/due", "kind": "latest", "enc": "json", "type": "lamp_msgs/ScheduleDue", "source": "schedule", "boot": "5c4ed01e5c4ed01e", "seq": 15, "at_us": 14000000, "at_unix_ms": 1792130414000}, "payload_b64": "eyJzdGFtcCI6eyJjbG9ja19pZCI6InNjaGVkdWxlIiwiYXRfdXMiOjE0MDAwMDAwLCJzZXF1ZW5jZSI6MTV9LCJzdG9yZV9pZCI6IjAxMjM0NTY3ODlhYmNkZWYwMTIzNDU2Nzg5YWJjZGVmIiwicmV2aXNpb24iOjMsImVuYWJsZWQiOnRydWUsInJlYWR5Ijp0cnVlLCJwZW5kaW5nIjowLCJ3YWl0aW5nX2Zvcl9jbG9jayI6MH0="} +{"header": {"topic": "schedule/due", "kind": "latest", "enc": "json", "type": "lamp_msgs/ScheduleDue", "source": "schedule", "boot": "5c4ed01e5c4ed01e", "seq": 16, "at_us": 15000000, "at_unix_ms": 1792130415000}, "payload_b64": "eyJzdGFtcCI6eyJjbG9ja19pZCI6InNjaGVkdWxlIiwiYXRfdXMiOjE1MDAwMDAwLCJzZXF1ZW5jZSI6MTZ9LCJzdG9yZV9pZCI6IjAxMjM0NTY3ODlhYmNkZWYwMTIzNDU2Nzg5YWJjZGVmIiwicmV2aXNpb24iOjMsImVuYWJsZWQiOnRydWUsInJlYWR5Ijp0cnVlLCJwZW5kaW5nIjowLCJ3YWl0aW5nX2Zvcl9jbG9jayI6MH0="} +{"header": {"topic": "schedule/due", "kind": "latest", "enc": "json", "type": "lamp_msgs/ScheduleDue", "source": "schedule", "boot": "5c4ed01e5c4ed01e", "seq": 17, "at_us": 16000000, "at_unix_ms": 1792130416000}, "payload_b64": "eyJzdGFtcCI6eyJjbG9ja19pZCI6InNjaGVkdWxlIiwiYXRfdXMiOjE2MDAwMDAwLCJzZXF1ZW5jZSI6MTd9LCJzdG9yZV9pZCI6IjAxMjM0NTY3ODlhYmNkZWYwMTIzNDU2Nzg5YWJjZGVmIiwicmV2aXNpb24iOjMsImVuYWJsZWQiOnRydWUsInJlYWR5Ijp0cnVlLCJwZW5kaW5nIjowLCJ3YWl0aW5nX2Zvcl9jbG9jayI6MH0="} +{"header": {"topic": "schedule/due", "kind": "latest", "enc": "json", "type": "lamp_msgs/ScheduleDue", "source": "schedule", "boot": "5c4ed01e5c4ed01e", "seq": 18, "at_us": 17000000, "at_unix_ms": 1792130417000}, "payload_b64": "eyJzdGFtcCI6eyJjbG9ja19pZCI6InNjaGVkdWxlIiwiYXRfdXMiOjE3MDAwMDAwLCJzZXF1ZW5jZSI6MTh9LCJzdG9yZV9pZCI6IjAxMjM0NTY3ODlhYmNkZWYwMTIzNDU2Nzg5YWJjZGVmIiwicmV2aXNpb24iOjMsImVuYWJsZWQiOnRydWUsInJlYWR5Ijp0cnVlLCJwZW5kaW5nIjowLCJ3YWl0aW5nX2Zvcl9jbG9jayI6MH0="} +{"header": {"topic": "schedule/due", "kind": "latest", "enc": "json", "type": "lamp_msgs/ScheduleDue", "source": "schedule", "boot": "5c4ed01e5c4ed01e", "seq": 19, "at_us": 18000000, "at_unix_ms": 1792130418000}, "payload_b64": "eyJzdGFtcCI6eyJjbG9ja19pZCI6InNjaGVkdWxlIiwiYXRfdXMiOjE4MDAwMDAwLCJzZXF1ZW5jZSI6MTl9LCJzdG9yZV9pZCI6IjAxMjM0NTY3ODlhYmNkZWYwMTIzNDU2Nzg5YWJjZGVmIiwicmV2aXNpb24iOjMsImVuYWJsZWQiOnRydWUsInJlYWR5Ijp0cnVlLCJwZW5kaW5nIjowLCJ3YWl0aW5nX2Zvcl9jbG9jayI6MH0="} +{"header": {"topic": "schedule/due", "kind": "latest", "enc": "json", "type": "lamp_msgs/ScheduleDue", "source": "schedule", "boot": "5c4ed01e5c4ed01e", "seq": 20, "at_us": 19000000, "at_unix_ms": 1792130419000}, "payload_b64": "eyJzdGFtcCI6eyJjbG9ja19pZCI6InNjaGVkdWxlIiwiYXRfdXMiOjE5MDAwMDAwLCJzZXF1ZW5jZSI6MjB9LCJzdG9yZV9pZCI6IjAxMjM0NTY3ODlhYmNkZWYwMTIzNDU2Nzg5YWJjZGVmIiwicmV2aXNpb24iOjMsImVuYWJsZWQiOnRydWUsInJlYWR5Ijp0cnVlLCJwZW5kaW5nIjowLCJ3YWl0aW5nX2Zvcl9jbG9jayI6MH0="} +{"header": {"topic": "schedule/due", "kind": "latest", "enc": "json", "type": "lamp_msgs/ScheduleDue", "source": "schedule", "boot": "5c4ed01e5c4ed01e", "seq": 21, "at_us": 20000000, "at_unix_ms": 1792130420000}, "payload_b64": "eyJzdGFtcCI6eyJjbG9ja19pZCI6InNjaGVkdWxlIiwiYXRfdXMiOjIwMDAwMDAwLCJzZXF1ZW5jZSI6MjF9LCJzdG9yZV9pZCI6IjAxMjM0NTY3ODlhYmNkZWYwMTIzNDU2Nzg5YWJjZGVmIiwicmV2aXNpb24iOjMsImVuYWJsZWQiOnRydWUsInJlYWR5Ijp0cnVlLCJwZW5kaW5nIjowLCJ3YWl0aW5nX2Zvcl9jbG9jayI6MH0="} +{"header": {"topic": "schedule/due", "kind": "latest", "enc": "json", "type": "lamp_msgs/ScheduleDue", "source": "schedule", "boot": "5c4ed01e5c4ed01e", "seq": 22, "at_us": 21000000, "at_unix_ms": 1792130421000}, "payload_b64": "eyJzdGFtcCI6eyJjbG9ja19pZCI6InNjaGVkdWxlIiwiYXRfdXMiOjIxMDAwMDAwLCJzZXF1ZW5jZSI6MjJ9LCJzdG9yZV9pZCI6IjAxMjM0NTY3ODlhYmNkZWYwMTIzNDU2Nzg5YWJjZGVmIiwicmV2aXNpb24iOjMsImVuYWJsZWQiOnRydWUsInJlYWR5Ijp0cnVlLCJwZW5kaW5nIjowLCJ3YWl0aW5nX2Zvcl9jbG9jayI6MH0="} +{"header": {"topic": "schedule/due", "kind": "latest", "enc": "json", "type": "lamp_msgs/ScheduleDue", "source": "schedule", "boot": "5c4ed01e5c4ed01e", "seq": 23, "at_us": 22000000, "at_unix_ms": 1792130422000}, "payload_b64": "eyJzdGFtcCI6eyJjbG9ja19pZCI6InNjaGVkdWxlIiwiYXRfdXMiOjIyMDAwMDAwLCJzZXF1ZW5jZSI6MjN9LCJzdG9yZV9pZCI6IjAxMjM0NTY3ODlhYmNkZWYwMTIzNDU2Nzg5YWJjZGVmIiwicmV2aXNpb24iOjMsImVuYWJsZWQiOnRydWUsInJlYWR5Ijp0cnVlLCJwZW5kaW5nIjowLCJ3YWl0aW5nX2Zvcl9jbG9jayI6MH0="} +{"header": {"topic": "schedule/due", "kind": "latest", "enc": "json", "type": "lamp_msgs/ScheduleDue", "source": "schedule", "boot": "5c4ed01e5c4ed01e", "seq": 24, "at_us": 23000000, "at_unix_ms": 1792130423000}, "payload_b64": "eyJzdGFtcCI6eyJjbG9ja19pZCI6InNjaGVkdWxlIiwiYXRfdXMiOjIzMDAwMDAwLCJzZXF1ZW5jZSI6MjR9LCJzdG9yZV9pZCI6IjAxMjM0NTY3ODlhYmNkZWYwMTIzNDU2Nzg5YWJjZGVmIiwicmV2aXNpb24iOjMsImVuYWJsZWQiOnRydWUsInJlYWR5Ijp0cnVlLCJwZW5kaW5nIjowLCJ3YWl0aW5nX2Zvcl9jbG9jayI6MH0="} +{"header": {"topic": "schedule/due", "kind": "latest", "enc": "json", "type": "lamp_msgs/ScheduleDue", "source": "schedule", "boot": "5c4ed01e5c4ed01e", "seq": 25, "at_us": 24000000, "at_unix_ms": 1792130424000}, "payload_b64": "eyJzdGFtcCI6eyJjbG9ja19pZCI6InNjaGVkdWxlIiwiYXRfdXMiOjI0MDAwMDAwLCJzZXF1ZW5jZSI6MjV9LCJzdG9yZV9pZCI6IjAxMjM0NTY3ODlhYmNkZWYwMTIzNDU2Nzg5YWJjZGVmIiwicmV2aXNpb24iOjMsImVuYWJsZWQiOnRydWUsInJlYWR5Ijp0cnVlLCJwZW5kaW5nIjowLCJ3YWl0aW5nX2Zvcl9jbG9jayI6MH0="} +{"header": {"topic": "schedule/due", "kind": "latest", "enc": "json", "type": "lamp_msgs/ScheduleDue", "source": "schedule", "boot": "5c4ed01e5c4ed01e", "seq": 26, "at_us": 25000000, "at_unix_ms": 1792130425000}, "payload_b64": "eyJzdGFtcCI6eyJjbG9ja19pZCI6InNjaGVkdWxlIiwiYXRfdXMiOjI1MDAwMDAwLCJzZXF1ZW5jZSI6MjZ9LCJzdG9yZV9pZCI6IjAxMjM0NTY3ODlhYmNkZWYwMTIzNDU2Nzg5YWJjZGVmIiwicmV2aXNpb24iOjMsImVuYWJsZWQiOnRydWUsInJlYWR5Ijp0cnVlLCJwZW5kaW5nIjowLCJ3YWl0aW5nX2Zvcl9jbG9jayI6MH0="} +{"header": {"topic": "schedule/due", "kind": "latest", "enc": "json", "type": "lamp_msgs/ScheduleDue", "source": "schedule", "boot": "5c4ed01e5c4ed01e", "seq": 27, "at_us": 26000000, "at_unix_ms": 1792130426000}, "payload_b64": "eyJzdGFtcCI6eyJjbG9ja19pZCI6InNjaGVkdWxlIiwiYXRfdXMiOjI2MDAwMDAwLCJzZXF1ZW5jZSI6Mjd9LCJzdG9yZV9pZCI6IjAxMjM0NTY3ODlhYmNkZWYwMTIzNDU2Nzg5YWJjZGVmIiwicmV2aXNpb24iOjMsImVuYWJsZWQiOnRydWUsInJlYWR5Ijp0cnVlLCJwZW5kaW5nIjowLCJ3YWl0aW5nX2Zvcl9jbG9jayI6MH0="} +{"header": {"topic": "schedule/due", "kind": "latest", "enc": "json", "type": "lamp_msgs/ScheduleDue", "source": "schedule", "boot": "5c4ed01e5c4ed01e", "seq": 28, "at_us": 27000000, "at_unix_ms": 1792130427000}, "payload_b64": "eyJzdGFtcCI6eyJjbG9ja19pZCI6InNjaGVkdWxlIiwiYXRfdXMiOjI3MDAwMDAwLCJzZXF1ZW5jZSI6Mjh9LCJzdG9yZV9pZCI6IjAxMjM0NTY3ODlhYmNkZWYwMTIzNDU2Nzg5YWJjZGVmIiwicmV2aXNpb24iOjMsImVuYWJsZWQiOnRydWUsInJlYWR5Ijp0cnVlLCJwZW5kaW5nIjowLCJ3YWl0aW5nX2Zvcl9jbG9jayI6MH0="} +{"header": {"topic": "schedule/due", "kind": "latest", "enc": "json", "type": "lamp_msgs/ScheduleDue", "source": "schedule", "boot": "5c4ed01e5c4ed01e", "seq": 29, "at_us": 28000000, "at_unix_ms": 1792130428000}, "payload_b64": "eyJzdGFtcCI6eyJjbG9ja19pZCI6InNjaGVkdWxlIiwiYXRfdXMiOjI4MDAwMDAwLCJzZXF1ZW5jZSI6Mjl9LCJzdG9yZV9pZCI6IjAxMjM0NTY3ODlhYmNkZWYwMTIzNDU2Nzg5YWJjZGVmIiwicmV2aXNpb24iOjMsImVuYWJsZWQiOnRydWUsInJlYWR5Ijp0cnVlLCJwZW5kaW5nIjowLCJ3YWl0aW5nX2Zvcl9jbG9jayI6MH0="} +{"header": {"topic": "schedule/due", "kind": "latest", "enc": "json", "type": "lamp_msgs/ScheduleDue", "source": "schedule", "boot": "5c4ed01e5c4ed01e", "seq": 30, "at_us": 29000000, "at_unix_ms": 1792130429000}, "payload_b64": "eyJzdGFtcCI6eyJjbG9ja19pZCI6InNjaGVkdWxlIiwiYXRfdXMiOjI5MDAwMDAwLCJzZXF1ZW5jZSI6MzB9LCJzdG9yZV9pZCI6IjAxMjM0NTY3ODlhYmNkZWYwMTIzNDU2Nzg5YWJjZGVmIiwicmV2aXNpb24iOjMsImVuYWJsZWQiOnRydWUsInJlYWR5Ijp0cnVlLCJwZW5kaW5nIjowLCJ3YWl0aW5nX2Zvcl9jbG9jayI6MH0="} +{"header": {"topic": "schedule/due", "kind": "latest", "enc": "json", "type": "lamp_msgs/ScheduleDue", "source": "schedule", "boot": "5c4ed01e5c4ed01e", "seq": 31, "at_us": 30000000, "at_unix_ms": 1792130430000}, "payload_b64": "eyJzdGFtcCI6eyJjbG9ja19pZCI6InNjaGVkdWxlIiwiYXRfdXMiOjMwMDAwMDAwLCJzZXF1ZW5jZSI6MzF9LCJzdG9yZV9pZCI6IjAxMjM0NTY3ODlhYmNkZWYwMTIzNDU2Nzg5YWJjZGVmIiwicmV2aXNpb24iOjMsImVuYWJsZWQiOnRydWUsInJlYWR5Ijp0cnVlLCJwZW5kaW5nIjoxLCJ3YWl0aW5nX2Zvcl9jbG9jayI6MCwib2xkZXN0Ijp7ImlkIjo3LCJkZWxpdmVyeSI6MSwiZHVlX3VuaXhfbXMiOjE3OTIxMzA0MzAwMDAsImxhdGUiOmZhbHNlfX0="} +{"header": {"topic": "schedule/due", "kind": "latest", "enc": "json", "type": "lamp_msgs/ScheduleDue", "source": "schedule", "boot": "5c4ed01e5c4ed01e", "seq": 32, "at_us": 31000000, "at_unix_ms": 1792130431000}, "payload_b64": "eyJzdGFtcCI6eyJjbG9ja19pZCI6InNjaGVkdWxlIiwiYXRfdXMiOjMxMDAwMDAwLCJzZXF1ZW5jZSI6MzJ9LCJzdG9yZV9pZCI6IjAxMjM0NTY3ODlhYmNkZWYwMTIzNDU2Nzg5YWJjZGVmIiwicmV2aXNpb24iOjMsImVuYWJsZWQiOnRydWUsInJlYWR5Ijp0cnVlLCJwZW5kaW5nIjoxLCJ3YWl0aW5nX2Zvcl9jbG9jayI6MCwib2xkZXN0Ijp7ImlkIjo3LCJkZWxpdmVyeSI6MSwiZHVlX3VuaXhfbXMiOjE3OTIxMzA0MzAwMDAsImxhdGUiOmZhbHNlfX0="} +{"header": {"topic": "schedule/due", "kind": "latest", "enc": "json", "type": "lamp_msgs/ScheduleDue", "source": "schedule", "boot": "5c4ed01e5c4ed01e", "seq": 33, "at_us": 32000000, "at_unix_ms": 1792130432000}, "payload_b64": "eyJzdGFtcCI6eyJjbG9ja19pZCI6InNjaGVkdWxlIiwiYXRfdXMiOjMyMDAwMDAwLCJzZXF1ZW5jZSI6MzN9LCJzdG9yZV9pZCI6IjAxMjM0NTY3ODlhYmNkZWYwMTIzNDU2Nzg5YWJjZGVmIiwicmV2aXNpb24iOjMsImVuYWJsZWQiOnRydWUsInJlYWR5Ijp0cnVlLCJwZW5kaW5nIjoxLCJ3YWl0aW5nX2Zvcl9jbG9jayI6MCwib2xkZXN0Ijp7ImlkIjo3LCJkZWxpdmVyeSI6MSwiZHVlX3VuaXhfbXMiOjE3OTIxMzA0MzAwMDAsImxhdGUiOmZhbHNlfX0="} +{"header": {"topic": "schedule/due", "kind": "latest", "enc": "json", "type": "lamp_msgs/ScheduleDue", "source": "schedule", "boot": "5c4ed01e5c4ed01e", "seq": 34, "at_us": 33000000, "at_unix_ms": 1792130433000}, "payload_b64": "eyJzdGFtcCI6eyJjbG9ja19pZCI6InNjaGVkdWxlIiwiYXRfdXMiOjMzMDAwMDAwLCJzZXF1ZW5jZSI6MzR9LCJzdG9yZV9pZCI6IjAxMjM0NTY3ODlhYmNkZWYwMTIzNDU2Nzg5YWJjZGVmIiwicmV2aXNpb24iOjMsImVuYWJsZWQiOnRydWUsInJlYWR5Ijp0cnVlLCJwZW5kaW5nIjoxLCJ3YWl0aW5nX2Zvcl9jbG9jayI6MCwib2xkZXN0Ijp7ImlkIjo3LCJkZWxpdmVyeSI6MSwiZHVlX3VuaXhfbXMiOjE3OTIxMzA0MzAwMDAsImxhdGUiOmZhbHNlfX0="} +{"header": {"topic": "schedule/due", "kind": "latest", "enc": "json", "type": "lamp_msgs/ScheduleDue", "source": "schedule", "boot": "5c4ed01e5c4ed01e", "seq": 35, "at_us": 34000000, "at_unix_ms": 1792130434000}, "payload_b64": "eyJzdGFtcCI6eyJjbG9ja19pZCI6InNjaGVkdWxlIiwiYXRfdXMiOjM0MDAwMDAwLCJzZXF1ZW5jZSI6MzV9LCJzdG9yZV9pZCI6IjAxMjM0NTY3ODlhYmNkZWYwMTIzNDU2Nzg5YWJjZGVmIiwicmV2aXNpb24iOjMsImVuYWJsZWQiOnRydWUsInJlYWR5Ijp0cnVlLCJwZW5kaW5nIjoxLCJ3YWl0aW5nX2Zvcl9jbG9jayI6MCwib2xkZXN0Ijp7ImlkIjo3LCJkZWxpdmVyeSI6MSwiZHVlX3VuaXhfbXMiOjE3OTIxMzA0MzAwMDAsImxhdGUiOmZhbHNlfX0="} +{"header": {"topic": "schedule/due", "kind": "latest", "enc": "json", "type": "lamp_msgs/ScheduleDue", "source": "schedule", "boot": "5c4ed01e5c4ed01e", "seq": 36, "at_us": 35000000, "at_unix_ms": 1792130435000}, "payload_b64": "eyJzdGFtcCI6eyJjbG9ja19pZCI6InNjaGVkdWxlIiwiYXRfdXMiOjM1MDAwMDAwLCJzZXF1ZW5jZSI6MzZ9LCJzdG9yZV9pZCI6IjAxMjM0NTY3ODlhYmNkZWYwMTIzNDU2Nzg5YWJjZGVmIiwicmV2aXNpb24iOjMsImVuYWJsZWQiOnRydWUsInJlYWR5Ijp0cnVlLCJwZW5kaW5nIjoxLCJ3YWl0aW5nX2Zvcl9jbG9jayI6MCwib2xkZXN0Ijp7ImlkIjo3LCJkZWxpdmVyeSI6MSwiZHVlX3VuaXhfbXMiOjE3OTIxMzA0MzAwMDAsImxhdGUiOmZhbHNlfX0="} +{"header": {"topic": "schedule/due", "kind": "latest", "enc": "json", "type": "lamp_msgs/ScheduleDue", "source": "schedule", "boot": "5c4ed01e5c4ed01e", "seq": 37, "at_us": 36000000, "at_unix_ms": 1792130436000}, "payload_b64": "eyJzdGFtcCI6eyJjbG9ja19pZCI6InNjaGVkdWxlIiwiYXRfdXMiOjM2MDAwMDAwLCJzZXF1ZW5jZSI6Mzd9LCJzdG9yZV9pZCI6IjAxMjM0NTY3ODlhYmNkZWYwMTIzNDU2Nzg5YWJjZGVmIiwicmV2aXNpb24iOjMsImVuYWJsZWQiOnRydWUsInJlYWR5Ijp0cnVlLCJwZW5kaW5nIjoxLCJ3YWl0aW5nX2Zvcl9jbG9jayI6MCwib2xkZXN0Ijp7ImlkIjo3LCJkZWxpdmVyeSI6MSwiZHVlX3VuaXhfbXMiOjE3OTIxMzA0MzAwMDAsImxhdGUiOmZhbHNlfX0="} +{"header": {"topic": "schedule/due", "kind": "latest", "enc": "json", "type": "lamp_msgs/ScheduleDue", "source": "schedule", "boot": "5c4ed01e5c4ed01e", "seq": 38, "at_us": 37000000, "at_unix_ms": 1792130437000}, "payload_b64": "eyJzdGFtcCI6eyJjbG9ja19pZCI6InNjaGVkdWxlIiwiYXRfdXMiOjM3MDAwMDAwLCJzZXF1ZW5jZSI6Mzh9LCJzdG9yZV9pZCI6IjAxMjM0NTY3ODlhYmNkZWYwMTIzNDU2Nzg5YWJjZGVmIiwicmV2aXNpb24iOjMsImVuYWJsZWQiOnRydWUsInJlYWR5Ijp0cnVlLCJwZW5kaW5nIjoxLCJ3YWl0aW5nX2Zvcl9jbG9jayI6MCwib2xkZXN0Ijp7ImlkIjo3LCJkZWxpdmVyeSI6MSwiZHVlX3VuaXhfbXMiOjE3OTIxMzA0MzAwMDAsImxhdGUiOmZhbHNlfX0="} +{"header": {"topic": "schedule/due", "kind": "latest", "enc": "json", "type": "lamp_msgs/ScheduleDue", "source": "schedule", "boot": "5c4ed01e5c4ed01e", "seq": 39, "at_us": 38000000, "at_unix_ms": 1792130438000}, "payload_b64": "eyJzdGFtcCI6eyJjbG9ja19pZCI6InNjaGVkdWxlIiwiYXRfdXMiOjM4MDAwMDAwLCJzZXF1ZW5jZSI6Mzl9LCJzdG9yZV9pZCI6IjAxMjM0NTY3ODlhYmNkZWYwMTIzNDU2Nzg5YWJjZGVmIiwicmV2aXNpb24iOjMsImVuYWJsZWQiOnRydWUsInJlYWR5Ijp0cnVlLCJwZW5kaW5nIjoxLCJ3YWl0aW5nX2Zvcl9jbG9jayI6MCwib2xkZXN0Ijp7ImlkIjo3LCJkZWxpdmVyeSI6MSwiZHVlX3VuaXhfbXMiOjE3OTIxMzA0MzAwMDAsImxhdGUiOmZhbHNlfX0="} +{"header": {"topic": "schedule/due", "kind": "latest", "enc": "json", "type": "lamp_msgs/ScheduleDue", "source": "schedule", "boot": "5c4ed01e5c4ed01e", "seq": 40, "at_us": 39000000, "at_unix_ms": 1792130439000}, "payload_b64": "eyJzdGFtcCI6eyJjbG9ja19pZCI6InNjaGVkdWxlIiwiYXRfdXMiOjM5MDAwMDAwLCJzZXF1ZW5jZSI6NDB9LCJzdG9yZV9pZCI6IjAxMjM0NTY3ODlhYmNkZWYwMTIzNDU2Nzg5YWJjZGVmIiwicmV2aXNpb24iOjMsImVuYWJsZWQiOnRydWUsInJlYWR5Ijp0cnVlLCJwZW5kaW5nIjoxLCJ3YWl0aW5nX2Zvcl9jbG9jayI6MCwib2xkZXN0Ijp7ImlkIjo3LCJkZWxpdmVyeSI6MSwiZHVlX3VuaXhfbXMiOjE3OTIxMzA0MzAwMDAsImxhdGUiOmZhbHNlfX0="} +{"header": {"topic": "schedule/due", "kind": "latest", "enc": "json", "type": "lamp_msgs/ScheduleDue", "source": "schedule", "boot": "5c4ed01e5c4ed01e", "seq": 41, "at_us": 40000000, "at_unix_ms": 1792130440000}, "payload_b64": "eyJzdGFtcCI6eyJjbG9ja19pZCI6InNjaGVkdWxlIiwiYXRfdXMiOjQwMDAwMDAwLCJzZXF1ZW5jZSI6NDF9LCJzdG9yZV9pZCI6IjAxMjM0NTY3ODlhYmNkZWYwMTIzNDU2Nzg5YWJjZGVmIiwicmV2aXNpb24iOjMsImVuYWJsZWQiOnRydWUsInJlYWR5Ijp0cnVlLCJwZW5kaW5nIjoxLCJ3YWl0aW5nX2Zvcl9jbG9jayI6MCwib2xkZXN0Ijp7ImlkIjo3LCJkZWxpdmVyeSI6MSwiZHVlX3VuaXhfbXMiOjE3OTIxMzA0MzAwMDAsImxhdGUiOmZhbHNlfX0="} +{"header": {"topic": "schedule/due", "kind": "latest", "enc": "json", "type": "lamp_msgs/ScheduleDue", "source": "schedule", "boot": "5c4ed01e5c4ed01e", "seq": 42, "at_us": 41000000, "at_unix_ms": 1792130441000}, "payload_b64": "eyJzdGFtcCI6eyJjbG9ja19pZCI6InNjaGVkdWxlIiwiYXRfdXMiOjQxMDAwMDAwLCJzZXF1ZW5jZSI6NDJ9LCJzdG9yZV9pZCI6IjAxMjM0NTY3ODlhYmNkZWYwMTIzNDU2Nzg5YWJjZGVmIiwicmV2aXNpb24iOjMsImVuYWJsZWQiOnRydWUsInJlYWR5Ijp0cnVlLCJwZW5kaW5nIjoxLCJ3YWl0aW5nX2Zvcl9jbG9jayI6MCwib2xkZXN0Ijp7ImlkIjo3LCJkZWxpdmVyeSI6MSwiZHVlX3VuaXhfbXMiOjE3OTIxMzA0MzAwMDAsImxhdGUiOmZhbHNlfX0="} +{"header": {"topic": "schedule/due", "kind": "latest", "enc": "json", "type": "lamp_msgs/ScheduleDue", "source": "schedule", "boot": "5c4ed01e5c4ed01e", "seq": 43, "at_us": 42000000, "at_unix_ms": 1792130442000}, "payload_b64": "eyJzdGFtcCI6eyJjbG9ja19pZCI6InNjaGVkdWxlIiwiYXRfdXMiOjQyMDAwMDAwLCJzZXF1ZW5jZSI6NDN9LCJzdG9yZV9pZCI6IjAxMjM0NTY3ODlhYmNkZWYwMTIzNDU2Nzg5YWJjZGVmIiwicmV2aXNpb24iOjMsImVuYWJsZWQiOnRydWUsInJlYWR5Ijp0cnVlLCJwZW5kaW5nIjoxLCJ3YWl0aW5nX2Zvcl9jbG9jayI6MCwib2xkZXN0Ijp7ImlkIjo3LCJkZWxpdmVyeSI6MSwiZHVlX3VuaXhfbXMiOjE3OTIxMzA0MzAwMDAsImxhdGUiOmZhbHNlfX0="} +{"header": {"topic": "schedule/due", "kind": "latest", "enc": "json", "type": "lamp_msgs/ScheduleDue", "source": "schedule", "boot": "5c4ed01e5c4ed01e", "seq": 44, "at_us": 43000000, "at_unix_ms": 1792130443000}, "payload_b64": "eyJzdGFtcCI6eyJjbG9ja19pZCI6InNjaGVkdWxlIiwiYXRfdXMiOjQzMDAwMDAwLCJzZXF1ZW5jZSI6NDR9LCJzdG9yZV9pZCI6IjAxMjM0NTY3ODlhYmNkZWYwMTIzNDU2Nzg5YWJjZGVmIiwicmV2aXNpb24iOjMsImVuYWJsZWQiOnRydWUsInJlYWR5Ijp0cnVlLCJwZW5kaW5nIjoxLCJ3YWl0aW5nX2Zvcl9jbG9jayI6MCwib2xkZXN0Ijp7ImlkIjo3LCJkZWxpdmVyeSI6MSwiZHVlX3VuaXhfbXMiOjE3OTIxMzA0MzAwMDAsImxhdGUiOmZhbHNlfX0="} +{"header": {"topic": "schedule/due", "kind": "latest", "enc": "json", "type": "lamp_msgs/ScheduleDue", "source": "schedule", "boot": "5c4ed01e5c4ed01e", "seq": 45, "at_us": 44000000, "at_unix_ms": 1792130444000}, "payload_b64": "eyJzdGFtcCI6eyJjbG9ja19pZCI6InNjaGVkdWxlIiwiYXRfdXMiOjQ0MDAwMDAwLCJzZXF1ZW5jZSI6NDV9LCJzdG9yZV9pZCI6IjAxMjM0NTY3ODlhYmNkZWYwMTIzNDU2Nzg5YWJjZGVmIiwicmV2aXNpb24iOjMsImVuYWJsZWQiOnRydWUsInJlYWR5Ijp0cnVlLCJwZW5kaW5nIjoxLCJ3YWl0aW5nX2Zvcl9jbG9jayI6MCwib2xkZXN0Ijp7ImlkIjo3LCJkZWxpdmVyeSI6MSwiZHVlX3VuaXhfbXMiOjE3OTIxMzA0MzAwMDAsImxhdGUiOmZhbHNlfX0="} +{"header": {"topic": "schedule/due", "kind": "latest", "enc": "json", "type": "lamp_msgs/ScheduleDue", "source": "schedule", "boot": "5c4ed01e5c4ed01e", "seq": 46, "at_us": 45000000, "at_unix_ms": 1792130445000}, "payload_b64": "eyJzdGFtcCI6eyJjbG9ja19pZCI6InNjaGVkdWxlIiwiYXRfdXMiOjQ1MDAwMDAwLCJzZXF1ZW5jZSI6NDZ9LCJzdG9yZV9pZCI6IjAxMjM0NTY3ODlhYmNkZWYwMTIzNDU2Nzg5YWJjZGVmIiwicmV2aXNpb24iOjMsImVuYWJsZWQiOnRydWUsInJlYWR5Ijp0cnVlLCJwZW5kaW5nIjoxLCJ3YWl0aW5nX2Zvcl9jbG9jayI6MCwib2xkZXN0Ijp7ImlkIjo3LCJkZWxpdmVyeSI6MSwiZHVlX3VuaXhfbXMiOjE3OTIxMzA0MzAwMDAsImxhdGUiOmZhbHNlfX0="} +{"header": {"topic": "schedule/due", "kind": "latest", "enc": "json", "type": "lamp_msgs/ScheduleDue", "source": "schedule", "boot": "5c4ed01e5c4ed01e", "seq": 47, "at_us": 46000000, "at_unix_ms": 1792130446000}, "payload_b64": "eyJzdGFtcCI6eyJjbG9ja19pZCI6InNjaGVkdWxlIiwiYXRfdXMiOjQ2MDAwMDAwLCJzZXF1ZW5jZSI6NDd9LCJzdG9yZV9pZCI6IjAxMjM0NTY3ODlhYmNkZWYwMTIzNDU2Nzg5YWJjZGVmIiwicmV2aXNpb24iOjMsImVuYWJsZWQiOnRydWUsInJlYWR5Ijp0cnVlLCJwZW5kaW5nIjoxLCJ3YWl0aW5nX2Zvcl9jbG9jayI6MCwib2xkZXN0Ijp7ImlkIjo3LCJkZWxpdmVyeSI6MSwiZHVlX3VuaXhfbXMiOjE3OTIxMzA0MzAwMDAsImxhdGUiOmZhbHNlfX0="} +{"header": {"topic": "schedule/due", "kind": "latest", "enc": "json", "type": "lamp_msgs/ScheduleDue", "source": "schedule", "boot": "5c4ed01e5c4ed01e", "seq": 48, "at_us": 47000000, "at_unix_ms": 1792130447000}, "payload_b64": "eyJzdGFtcCI6eyJjbG9ja19pZCI6InNjaGVkdWxlIiwiYXRfdXMiOjQ3MDAwMDAwLCJzZXF1ZW5jZSI6NDh9LCJzdG9yZV9pZCI6IjAxMjM0NTY3ODlhYmNkZWYwMTIzNDU2Nzg5YWJjZGVmIiwicmV2aXNpb24iOjMsImVuYWJsZWQiOnRydWUsInJlYWR5Ijp0cnVlLCJwZW5kaW5nIjoxLCJ3YWl0aW5nX2Zvcl9jbG9jayI6MCwib2xkZXN0Ijp7ImlkIjo3LCJkZWxpdmVyeSI6MSwiZHVlX3VuaXhfbXMiOjE3OTIxMzA0MzAwMDAsImxhdGUiOmZhbHNlfX0="} +{"header": {"topic": "schedule/due", "kind": "latest", "enc": "json", "type": "lamp_msgs/ScheduleDue", "source": "schedule", "boot": "5c4ed01e5c4ed01e", "seq": 49, "at_us": 48000000, "at_unix_ms": 1792130448000}, "payload_b64": "eyJzdGFtcCI6eyJjbG9ja19pZCI6InNjaGVkdWxlIiwiYXRfdXMiOjQ4MDAwMDAwLCJzZXF1ZW5jZSI6NDl9LCJzdG9yZV9pZCI6IjAxMjM0NTY3ODlhYmNkZWYwMTIzNDU2Nzg5YWJjZGVmIiwicmV2aXNpb24iOjMsImVuYWJsZWQiOnRydWUsInJlYWR5Ijp0cnVlLCJwZW5kaW5nIjoxLCJ3YWl0aW5nX2Zvcl9jbG9jayI6MCwib2xkZXN0Ijp7ImlkIjo3LCJkZWxpdmVyeSI6MSwiZHVlX3VuaXhfbXMiOjE3OTIxMzA0MzAwMDAsImxhdGUiOmZhbHNlfX0="} +{"header": {"topic": "schedule/due", "kind": "latest", "enc": "json", "type": "lamp_msgs/ScheduleDue", "source": "schedule", "boot": "5c4ed01e5c4ed01e", "seq": 50, "at_us": 49000000, "at_unix_ms": 1792130449000}, "payload_b64": "eyJzdGFtcCI6eyJjbG9ja19pZCI6InNjaGVkdWxlIiwiYXRfdXMiOjQ5MDAwMDAwLCJzZXF1ZW5jZSI6NTB9LCJzdG9yZV9pZCI6IjAxMjM0NTY3ODlhYmNkZWYwMTIzNDU2Nzg5YWJjZGVmIiwicmV2aXNpb24iOjMsImVuYWJsZWQiOnRydWUsInJlYWR5Ijp0cnVlLCJwZW5kaW5nIjoxLCJ3YWl0aW5nX2Zvcl9jbG9jayI6MCwib2xkZXN0Ijp7ImlkIjo3LCJkZWxpdmVyeSI6MSwiZHVlX3VuaXhfbXMiOjE3OTIxMzA0MzAwMDAsImxhdGUiOmZhbHNlfX0="} +{"header": {"topic": "schedule/due", "kind": "latest", "enc": "json", "type": "lamp_msgs/ScheduleDue", "source": "schedule", "boot": "5c4ed01e5c4ed01e", "seq": 51, "at_us": 50000000, "at_unix_ms": 1792130450000}, "payload_b64": "eyJzdGFtcCI6eyJjbG9ja19pZCI6InNjaGVkdWxlIiwiYXRfdXMiOjUwMDAwMDAwLCJzZXF1ZW5jZSI6NTF9LCJzdG9yZV9pZCI6IjAxMjM0NTY3ODlhYmNkZWYwMTIzNDU2Nzg5YWJjZGVmIiwicmV2aXNpb24iOjMsImVuYWJsZWQiOnRydWUsInJlYWR5Ijp0cnVlLCJwZW5kaW5nIjoxLCJ3YWl0aW5nX2Zvcl9jbG9jayI6MCwib2xkZXN0Ijp7ImlkIjo3LCJkZWxpdmVyeSI6MSwiZHVlX3VuaXhfbXMiOjE3OTIxMzA0MzAwMDAsImxhdGUiOmZhbHNlfX0="} +{"header": {"topic": "schedule/due", "kind": "latest", "enc": "json", "type": "lamp_msgs/ScheduleDue", "source": "schedule", "boot": "5c4ed01e5c4ed01e", "seq": 52, "at_us": 51000000, "at_unix_ms": 1792130451000}, "payload_b64": "eyJzdGFtcCI6eyJjbG9ja19pZCI6InNjaGVkdWxlIiwiYXRfdXMiOjUxMDAwMDAwLCJzZXF1ZW5jZSI6NTJ9LCJzdG9yZV9pZCI6IjAxMjM0NTY3ODlhYmNkZWYwMTIzNDU2Nzg5YWJjZGVmIiwicmV2aXNpb24iOjMsImVuYWJsZWQiOnRydWUsInJlYWR5Ijp0cnVlLCJwZW5kaW5nIjoxLCJ3YWl0aW5nX2Zvcl9jbG9jayI6MCwib2xkZXN0Ijp7ImlkIjo3LCJkZWxpdmVyeSI6MSwiZHVlX3VuaXhfbXMiOjE3OTIxMzA0MzAwMDAsImxhdGUiOmZhbHNlfX0="} +{"header": {"topic": "schedule/due", "kind": "latest", "enc": "json", "type": "lamp_msgs/ScheduleDue", "source": "schedule", "boot": "5c4ed01e5c4ed01e", "seq": 53, "at_us": 52000000, "at_unix_ms": 1792130452000}, "payload_b64": "eyJzdGFtcCI6eyJjbG9ja19pZCI6InNjaGVkdWxlIiwiYXRfdXMiOjUyMDAwMDAwLCJzZXF1ZW5jZSI6NTN9LCJzdG9yZV9pZCI6IjAxMjM0NTY3ODlhYmNkZWYwMTIzNDU2Nzg5YWJjZGVmIiwicmV2aXNpb24iOjMsImVuYWJsZWQiOnRydWUsInJlYWR5Ijp0cnVlLCJwZW5kaW5nIjoxLCJ3YWl0aW5nX2Zvcl9jbG9jayI6MCwib2xkZXN0Ijp7ImlkIjo3LCJkZWxpdmVyeSI6MSwiZHVlX3VuaXhfbXMiOjE3OTIxMzA0MzAwMDAsImxhdGUiOmZhbHNlfX0="} +{"header": {"topic": "schedule/due", "kind": "latest", "enc": "json", "type": "lamp_msgs/ScheduleDue", "source": "schedule", "boot": "5c4ed01e5c4ed01e", "seq": 54, "at_us": 53000000, "at_unix_ms": 1792130453000}, "payload_b64": "eyJzdGFtcCI6eyJjbG9ja19pZCI6InNjaGVkdWxlIiwiYXRfdXMiOjUzMDAwMDAwLCJzZXF1ZW5jZSI6NTR9LCJzdG9yZV9pZCI6IjAxMjM0NTY3ODlhYmNkZWYwMTIzNDU2Nzg5YWJjZGVmIiwicmV2aXNpb24iOjMsImVuYWJsZWQiOnRydWUsInJlYWR5Ijp0cnVlLCJwZW5kaW5nIjoxLCJ3YWl0aW5nX2Zvcl9jbG9jayI6MCwib2xkZXN0Ijp7ImlkIjo3LCJkZWxpdmVyeSI6MSwiZHVlX3VuaXhfbXMiOjE3OTIxMzA0MzAwMDAsImxhdGUiOmZhbHNlfX0="} +{"header": {"topic": "schedule/due", "kind": "latest", "enc": "json", "type": "lamp_msgs/ScheduleDue", "source": "schedule", "boot": "5c4ed01e5c4ed01e", "seq": 55, "at_us": 54000000, "at_unix_ms": 1792130454000}, "payload_b64": "eyJzdGFtcCI6eyJjbG9ja19pZCI6InNjaGVkdWxlIiwiYXRfdXMiOjU0MDAwMDAwLCJzZXF1ZW5jZSI6NTV9LCJzdG9yZV9pZCI6IjAxMjM0NTY3ODlhYmNkZWYwMTIzNDU2Nzg5YWJjZGVmIiwicmV2aXNpb24iOjMsImVuYWJsZWQiOnRydWUsInJlYWR5Ijp0cnVlLCJwZW5kaW5nIjoxLCJ3YWl0aW5nX2Zvcl9jbG9jayI6MCwib2xkZXN0Ijp7ImlkIjo3LCJkZWxpdmVyeSI6MSwiZHVlX3VuaXhfbXMiOjE3OTIxMzA0MzAwMDAsImxhdGUiOmZhbHNlfX0="} +{"header": {"topic": "schedule/due", "kind": "latest", "enc": "json", "type": "lamp_msgs/ScheduleDue", "source": "schedule", "boot": "5c4ed01e5c4ed01e", "seq": 56, "at_us": 55000000, "at_unix_ms": 1792130455000}, "payload_b64": "eyJzdGFtcCI6eyJjbG9ja19pZCI6InNjaGVkdWxlIiwiYXRfdXMiOjU1MDAwMDAwLCJzZXF1ZW5jZSI6NTZ9LCJzdG9yZV9pZCI6IjAxMjM0NTY3ODlhYmNkZWYwMTIzNDU2Nzg5YWJjZGVmIiwicmV2aXNpb24iOjMsImVuYWJsZWQiOnRydWUsInJlYWR5Ijp0cnVlLCJwZW5kaW5nIjoxLCJ3YWl0aW5nX2Zvcl9jbG9jayI6MCwib2xkZXN0Ijp7ImlkIjo3LCJkZWxpdmVyeSI6MSwiZHVlX3VuaXhfbXMiOjE3OTIxMzA0MzAwMDAsImxhdGUiOmZhbHNlfX0="} +{"header": {"topic": "schedule/due", "kind": "latest", "enc": "json", "type": "lamp_msgs/ScheduleDue", "source": "schedule", "boot": "5c4ed01e5c4ed01e", "seq": 57, "at_us": 56000000, "at_unix_ms": 1792130456000}, "payload_b64": "eyJzdGFtcCI6eyJjbG9ja19pZCI6InNjaGVkdWxlIiwiYXRfdXMiOjU2MDAwMDAwLCJzZXF1ZW5jZSI6NTd9LCJzdG9yZV9pZCI6IjAxMjM0NTY3ODlhYmNkZWYwMTIzNDU2Nzg5YWJjZGVmIiwicmV2aXNpb24iOjMsImVuYWJsZWQiOnRydWUsInJlYWR5Ijp0cnVlLCJwZW5kaW5nIjoxLCJ3YWl0aW5nX2Zvcl9jbG9jayI6MCwib2xkZXN0Ijp7ImlkIjo3LCJkZWxpdmVyeSI6MSwiZHVlX3VuaXhfbXMiOjE3OTIxMzA0MzAwMDAsImxhdGUiOmZhbHNlfX0="} +{"header": {"topic": "schedule/due", "kind": "latest", "enc": "json", "type": "lamp_msgs/ScheduleDue", "source": "schedule", "boot": "5c4ed01e5c4ed01e", "seq": 58, "at_us": 57000000, "at_unix_ms": 1792130457000}, "payload_b64": "eyJzdGFtcCI6eyJjbG9ja19pZCI6InNjaGVkdWxlIiwiYXRfdXMiOjU3MDAwMDAwLCJzZXF1ZW5jZSI6NTh9LCJzdG9yZV9pZCI6IjAxMjM0NTY3ODlhYmNkZWYwMTIzNDU2Nzg5YWJjZGVmIiwicmV2aXNpb24iOjMsImVuYWJsZWQiOnRydWUsInJlYWR5Ijp0cnVlLCJwZW5kaW5nIjoxLCJ3YWl0aW5nX2Zvcl9jbG9jayI6MCwib2xkZXN0Ijp7ImlkIjo3LCJkZWxpdmVyeSI6MSwiZHVlX3VuaXhfbXMiOjE3OTIxMzA0MzAwMDAsImxhdGUiOmZhbHNlfX0="} +{"header": {"topic": "schedule/due", "kind": "latest", "enc": "json", "type": "lamp_msgs/ScheduleDue", "source": "schedule", "boot": "5c4ed01e5c4ed01e", "seq": 59, "at_us": 58000000, "at_unix_ms": 1792130458000}, "payload_b64": "eyJzdGFtcCI6eyJjbG9ja19pZCI6InNjaGVkdWxlIiwiYXRfdXMiOjU4MDAwMDAwLCJzZXF1ZW5jZSI6NTl9LCJzdG9yZV9pZCI6IjAxMjM0NTY3ODlhYmNkZWYwMTIzNDU2Nzg5YWJjZGVmIiwicmV2aXNpb24iOjMsImVuYWJsZWQiOnRydWUsInJlYWR5Ijp0cnVlLCJwZW5kaW5nIjoxLCJ3YWl0aW5nX2Zvcl9jbG9jayI6MCwib2xkZXN0Ijp7ImlkIjo3LCJkZWxpdmVyeSI6MSwiZHVlX3VuaXhfbXMiOjE3OTIxMzA0MzAwMDAsImxhdGUiOmZhbHNlfX0="} +{"header": {"topic": "schedule/due", "kind": "latest", "enc": "json", "type": "lamp_msgs/ScheduleDue", "source": "schedule", "boot": "5c4ed01e5c4ed01e", "seq": 60, "at_us": 59000000, "at_unix_ms": 1792130459000}, "payload_b64": "eyJzdGFtcCI6eyJjbG9ja19pZCI6InNjaGVkdWxlIiwiYXRfdXMiOjU5MDAwMDAwLCJzZXF1ZW5jZSI6NjB9LCJzdG9yZV9pZCI6IjAxMjM0NTY3ODlhYmNkZWYwMTIzNDU2Nzg5YWJjZGVmIiwicmV2aXNpb24iOjMsImVuYWJsZWQiOnRydWUsInJlYWR5Ijp0cnVlLCJwZW5kaW5nIjoxLCJ3YWl0aW5nX2Zvcl9jbG9jayI6MCwib2xkZXN0Ijp7ImlkIjo3LCJkZWxpdmVyeSI6MSwiZHVlX3VuaXhfbXMiOjE3OTIxMzA0MzAwMDAsImxhdGUiOmZhbHNlfX0="} +{"header": {"topic": "schedule/due", "kind": "latest", "enc": "json", "type": "lamp_msgs/ScheduleDue", "source": "schedule", "boot": "5c4ed01e5c4ed01e", "seq": 61, "at_us": 60000000, "at_unix_ms": 1792130460000}, "payload_b64": "eyJzdGFtcCI6eyJjbG9ja19pZCI6InNjaGVkdWxlIiwiYXRfdXMiOjYwMDAwMDAwLCJzZXF1ZW5jZSI6NjF9LCJzdG9yZV9pZCI6IjAxMjM0NTY3ODlhYmNkZWYwMTIzNDU2Nzg5YWJjZGVmIiwicmV2aXNpb24iOjMsImVuYWJsZWQiOnRydWUsInJlYWR5Ijp0cnVlLCJwZW5kaW5nIjoxLCJ3YWl0aW5nX2Zvcl9jbG9jayI6MCwib2xkZXN0Ijp7ImlkIjo3LCJkZWxpdmVyeSI6MSwiZHVlX3VuaXhfbXMiOjE3OTIxMzA0MzAwMDAsImxhdGUiOmZhbHNlfX0="} +{"header": {"topic": "schedule/due", "kind": "latest", "enc": "json", "type": "lamp_msgs/ScheduleDue", "source": "schedule", "boot": "5c4ed01e5c4ed01e", "seq": 62, "at_us": 61000000, "at_unix_ms": 1792130461000}, "payload_b64": "eyJzdGFtcCI6eyJjbG9ja19pZCI6InNjaGVkdWxlIiwiYXRfdXMiOjYxMDAwMDAwLCJzZXF1ZW5jZSI6NjJ9LCJzdG9yZV9pZCI6IjAxMjM0NTY3ODlhYmNkZWYwMTIzNDU2Nzg5YWJjZGVmIiwicmV2aXNpb24iOjMsImVuYWJsZWQiOnRydWUsInJlYWR5Ijp0cnVlLCJwZW5kaW5nIjoxLCJ3YWl0aW5nX2Zvcl9jbG9jayI6MCwib2xkZXN0Ijp7ImlkIjo3LCJkZWxpdmVyeSI6MSwiZHVlX3VuaXhfbXMiOjE3OTIxMzA0MzAwMDAsImxhdGUiOmZhbHNlfX0="} +{"header": {"topic": "schedule/due", "kind": "latest", "enc": "json", "type": "lamp_msgs/ScheduleDue", "source": "schedule", "boot": "5c4ed01e5c4ed01e", "seq": 63, "at_us": 62000000, "at_unix_ms": 1792130462000}, "payload_b64": "eyJzdGFtcCI6eyJjbG9ja19pZCI6InNjaGVkdWxlIiwiYXRfdXMiOjYyMDAwMDAwLCJzZXF1ZW5jZSI6NjN9LCJzdG9yZV9pZCI6IjAxMjM0NTY3ODlhYmNkZWYwMTIzNDU2Nzg5YWJjZGVmIiwicmV2aXNpb24iOjMsImVuYWJsZWQiOnRydWUsInJlYWR5Ijp0cnVlLCJwZW5kaW5nIjoxLCJ3YWl0aW5nX2Zvcl9jbG9jayI6MCwib2xkZXN0Ijp7ImlkIjo3LCJkZWxpdmVyeSI6MSwiZHVlX3VuaXhfbXMiOjE3OTIxMzA0MzAwMDAsImxhdGUiOmZhbHNlfX0="} +{"header": {"topic": "schedule/due", "kind": "latest", "enc": "json", "type": "lamp_msgs/ScheduleDue", "source": "schedule", "boot": "5c4ed01e5c4ed01e", "seq": 64, "at_us": 63000000, "at_unix_ms": 1792130463000}, "payload_b64": "eyJzdGFtcCI6eyJjbG9ja19pZCI6InNjaGVkdWxlIiwiYXRfdXMiOjYzMDAwMDAwLCJzZXF1ZW5jZSI6NjR9LCJzdG9yZV9pZCI6IjAxMjM0NTY3ODlhYmNkZWYwMTIzNDU2Nzg5YWJjZGVmIiwicmV2aXNpb24iOjMsImVuYWJsZWQiOnRydWUsInJlYWR5Ijp0cnVlLCJwZW5kaW5nIjoxLCJ3YWl0aW5nX2Zvcl9jbG9jayI6MCwib2xkZXN0Ijp7ImlkIjo3LCJkZWxpdmVyeSI6MSwiZHVlX3VuaXhfbXMiOjE3OTIxMzA0MzAwMDAsImxhdGUiOmZhbHNlfX0="} +{"header": {"topic": "schedule/due", "kind": "latest", "enc": "json", "type": "lamp_msgs/ScheduleDue", "source": "schedule", "boot": "5c4ed01e5c4ed01e", "seq": 65, "at_us": 64000000, "at_unix_ms": 1792130464000}, "payload_b64": "eyJzdGFtcCI6eyJjbG9ja19pZCI6InNjaGVkdWxlIiwiYXRfdXMiOjY0MDAwMDAwLCJzZXF1ZW5jZSI6NjV9LCJzdG9yZV9pZCI6IjAxMjM0NTY3ODlhYmNkZWYwMTIzNDU2Nzg5YWJjZGVmIiwicmV2aXNpb24iOjMsImVuYWJsZWQiOnRydWUsInJlYWR5Ijp0cnVlLCJwZW5kaW5nIjoxLCJ3YWl0aW5nX2Zvcl9jbG9jayI6MCwib2xkZXN0Ijp7ImlkIjo3LCJkZWxpdmVyeSI6MSwiZHVlX3VuaXhfbXMiOjE3OTIxMzA0MzAwMDAsImxhdGUiOmZhbHNlfX0="} +{"header": {"topic": "schedule/due", "kind": "latest", "enc": "json", "type": "lamp_msgs/ScheduleDue", "source": "schedule", "boot": "5c4ed01e5c4ed01e", "seq": 66, "at_us": 65000000, "at_unix_ms": 1792130465000}, "payload_b64": "eyJzdGFtcCI6eyJjbG9ja19pZCI6InNjaGVkdWxlIiwiYXRfdXMiOjY1MDAwMDAwLCJzZXF1ZW5jZSI6NjZ9LCJzdG9yZV9pZCI6IjAxMjM0NTY3ODlhYmNkZWYwMTIzNDU2Nzg5YWJjZGVmIiwicmV2aXNpb24iOjMsImVuYWJsZWQiOnRydWUsInJlYWR5Ijp0cnVlLCJwZW5kaW5nIjoxLCJ3YWl0aW5nX2Zvcl9jbG9jayI6MCwib2xkZXN0Ijp7ImlkIjo3LCJkZWxpdmVyeSI6MSwiZHVlX3VuaXhfbXMiOjE3OTIxMzA0MzAwMDAsImxhdGUiOmZhbHNlfX0="} +{"header": {"topic": "schedule/due", "kind": "latest", "enc": "json", "type": "lamp_msgs/ScheduleDue", "source": "schedule", "boot": "5c4ed01e5c4ed01e", "seq": 67, "at_us": 66000000, "at_unix_ms": 1792130466000}, "payload_b64": "eyJzdGFtcCI6eyJjbG9ja19pZCI6InNjaGVkdWxlIiwiYXRfdXMiOjY2MDAwMDAwLCJzZXF1ZW5jZSI6Njd9LCJzdG9yZV9pZCI6IjAxMjM0NTY3ODlhYmNkZWYwMTIzNDU2Nzg5YWJjZGVmIiwicmV2aXNpb24iOjMsImVuYWJsZWQiOnRydWUsInJlYWR5Ijp0cnVlLCJwZW5kaW5nIjoxLCJ3YWl0aW5nX2Zvcl9jbG9jayI6MCwib2xkZXN0Ijp7ImlkIjo3LCJkZWxpdmVyeSI6MSwiZHVlX3VuaXhfbXMiOjE3OTIxMzA0MzAwMDAsImxhdGUiOmZhbHNlfX0="} +{"header": {"topic": "schedule/due", "kind": "latest", "enc": "json", "type": "lamp_msgs/ScheduleDue", "source": "schedule", "boot": "5c4ed01e5c4ed01e", "seq": 68, "at_us": 67000000, "at_unix_ms": 1792130467000}, "payload_b64": "eyJzdGFtcCI6eyJjbG9ja19pZCI6InNjaGVkdWxlIiwiYXRfdXMiOjY3MDAwMDAwLCJzZXF1ZW5jZSI6Njh9LCJzdG9yZV9pZCI6IjAxMjM0NTY3ODlhYmNkZWYwMTIzNDU2Nzg5YWJjZGVmIiwicmV2aXNpb24iOjMsImVuYWJsZWQiOnRydWUsInJlYWR5Ijp0cnVlLCJwZW5kaW5nIjoxLCJ3YWl0aW5nX2Zvcl9jbG9jayI6MCwib2xkZXN0Ijp7ImlkIjo3LCJkZWxpdmVyeSI6MSwiZHVlX3VuaXhfbXMiOjE3OTIxMzA0MzAwMDAsImxhdGUiOmZhbHNlfX0="} +{"header": {"topic": "schedule/due", "kind": "latest", "enc": "json", "type": "lamp_msgs/ScheduleDue", "source": "schedule", "boot": "5c4ed01e5c4ed01e", "seq": 69, "at_us": 68000000, "at_unix_ms": 1792130468000}, "payload_b64": "eyJzdGFtcCI6eyJjbG9ja19pZCI6InNjaGVkdWxlIiwiYXRfdXMiOjY4MDAwMDAwLCJzZXF1ZW5jZSI6Njl9LCJzdG9yZV9pZCI6IjAxMjM0NTY3ODlhYmNkZWYwMTIzNDU2Nzg5YWJjZGVmIiwicmV2aXNpb24iOjMsImVuYWJsZWQiOnRydWUsInJlYWR5Ijp0cnVlLCJwZW5kaW5nIjoxLCJ3YWl0aW5nX2Zvcl9jbG9jayI6MCwib2xkZXN0Ijp7ImlkIjo3LCJkZWxpdmVyeSI6MSwiZHVlX3VuaXhfbXMiOjE3OTIxMzA0MzAwMDAsImxhdGUiOmZhbHNlfX0="} +{"header": {"topic": "schedule/due", "kind": "latest", "enc": "json", "type": "lamp_msgs/ScheduleDue", "source": "schedule", "boot": "5c4ed01e5c4ed01e", "seq": 70, "at_us": 69000000, "at_unix_ms": 1792130469000}, "payload_b64": "eyJzdGFtcCI6eyJjbG9ja19pZCI6InNjaGVkdWxlIiwiYXRfdXMiOjY5MDAwMDAwLCJzZXF1ZW5jZSI6NzB9LCJzdG9yZV9pZCI6IjAxMjM0NTY3ODlhYmNkZWYwMTIzNDU2Nzg5YWJjZGVmIiwicmV2aXNpb24iOjMsImVuYWJsZWQiOnRydWUsInJlYWR5Ijp0cnVlLCJwZW5kaW5nIjoxLCJ3YWl0aW5nX2Zvcl9jbG9jayI6MCwib2xkZXN0Ijp7ImlkIjo3LCJkZWxpdmVyeSI6MSwiZHVlX3VuaXhfbXMiOjE3OTIxMzA0MzAwMDAsImxhdGUiOmZhbHNlfX0="} +{"header": {"topic": "schedule/due", "kind": "latest", "enc": "json", "type": "lamp_msgs/ScheduleDue", "source": "schedule", "boot": "5c4ed01e5c4ed01e", "seq": 71, "at_us": 70000000, "at_unix_ms": 1792130470000}, "payload_b64": "eyJzdGFtcCI6eyJjbG9ja19pZCI6InNjaGVkdWxlIiwiYXRfdXMiOjcwMDAwMDAwLCJzZXF1ZW5jZSI6NzF9LCJzdG9yZV9pZCI6IjAxMjM0NTY3ODlhYmNkZWYwMTIzNDU2Nzg5YWJjZGVmIiwicmV2aXNpb24iOjMsImVuYWJsZWQiOnRydWUsInJlYWR5Ijp0cnVlLCJwZW5kaW5nIjowLCJ3YWl0aW5nX2Zvcl9jbG9jayI6MH0="} +{"header": {"topic": "schedule/due", "kind": "latest", "enc": "json", "type": "lamp_msgs/ScheduleDue", "source": "schedule", "boot": "5c4ed01e5c4ed01e", "seq": 72, "at_us": 71000000, "at_unix_ms": 1792130471000}, "payload_b64": "eyJzdGFtcCI6eyJjbG9ja19pZCI6InNjaGVkdWxlIiwiYXRfdXMiOjcxMDAwMDAwLCJzZXF1ZW5jZSI6NzJ9LCJzdG9yZV9pZCI6IjAxMjM0NTY3ODlhYmNkZWYwMTIzNDU2Nzg5YWJjZGVmIiwicmV2aXNpb24iOjMsImVuYWJsZWQiOnRydWUsInJlYWR5Ijp0cnVlLCJwZW5kaW5nIjowLCJ3YWl0aW5nX2Zvcl9jbG9jayI6MH0="} +{"header": {"topic": "schedule/due", "kind": "latest", "enc": "json", "type": "lamp_msgs/ScheduleDue", "source": "schedule", "boot": "5c4ed01e5c4ed01e", "seq": 73, "at_us": 72000000, "at_unix_ms": 1792130472000}, "payload_b64": "eyJzdGFtcCI6eyJjbG9ja19pZCI6InNjaGVkdWxlIiwiYXRfdXMiOjcyMDAwMDAwLCJzZXF1ZW5jZSI6NzN9LCJzdG9yZV9pZCI6IjAxMjM0NTY3ODlhYmNkZWYwMTIzNDU2Nzg5YWJjZGVmIiwicmV2aXNpb24iOjMsImVuYWJsZWQiOnRydWUsInJlYWR5Ijp0cnVlLCJwZW5kaW5nIjowLCJ3YWl0aW5nX2Zvcl9jbG9jayI6MH0="} +{"header": {"topic": "schedule/due", "kind": "latest", "enc": "json", "type": "lamp_msgs/ScheduleDue", "source": "schedule", "boot": "5c4ed01e5c4ed01e", "seq": 74, "at_us": 73000000, "at_unix_ms": 1792130473000}, "payload_b64": "eyJzdGFtcCI6eyJjbG9ja19pZCI6InNjaGVkdWxlIiwiYXRfdXMiOjczMDAwMDAwLCJzZXF1ZW5jZSI6NzR9LCJzdG9yZV9pZCI6IjAxMjM0NTY3ODlhYmNkZWYwMTIzNDU2Nzg5YWJjZGVmIiwicmV2aXNpb24iOjMsImVuYWJsZWQiOnRydWUsInJlYWR5Ijp0cnVlLCJwZW5kaW5nIjowLCJ3YWl0aW5nX2Zvcl9jbG9jayI6MH0="} +{"header": {"topic": "schedule/due", "kind": "latest", "enc": "json", "type": "lamp_msgs/ScheduleDue", "source": "schedule", "boot": "5c4ed01e5c4ed01e", "seq": 75, "at_us": 74000000, "at_unix_ms": 1792130474000}, "payload_b64": "eyJzdGFtcCI6eyJjbG9ja19pZCI6InNjaGVkdWxlIiwiYXRfdXMiOjc0MDAwMDAwLCJzZXF1ZW5jZSI6NzV9LCJzdG9yZV9pZCI6IjAxMjM0NTY3ODlhYmNkZWYwMTIzNDU2Nzg5YWJjZGVmIiwicmV2aXNpb24iOjMsImVuYWJsZWQiOnRydWUsInJlYWR5Ijp0cnVlLCJwZW5kaW5nIjowLCJ3YWl0aW5nX2Zvcl9jbG9jayI6MH0="} +{"header": {"topic": "schedule/due", "kind": "latest", "enc": "json", "type": "lamp_msgs/ScheduleDue", "source": "schedule", "boot": "5c4ed01e5c4ed01e", "seq": 76, "at_us": 75000000, "at_unix_ms": 1792130475000}, "payload_b64": "eyJzdGFtcCI6eyJjbG9ja19pZCI6InNjaGVkdWxlIiwiYXRfdXMiOjc1MDAwMDAwLCJzZXF1ZW5jZSI6NzZ9LCJzdG9yZV9pZCI6IjAxMjM0NTY3ODlhYmNkZWYwMTIzNDU2Nzg5YWJjZGVmIiwicmV2aXNpb24iOjMsImVuYWJsZWQiOnRydWUsInJlYWR5Ijp0cnVlLCJwZW5kaW5nIjowLCJ3YWl0aW5nX2Zvcl9jbG9jayI6MH0="} +{"header": {"topic": "schedule/due", "kind": "latest", "enc": "json", "type": "lamp_msgs/ScheduleDue", "source": "schedule", "boot": "5c4ed01e5c4ed01e", "seq": 77, "at_us": 76000000, "at_unix_ms": 1792130476000}, "payload_b64": "eyJzdGFtcCI6eyJjbG9ja19pZCI6InNjaGVkdWxlIiwiYXRfdXMiOjc2MDAwMDAwLCJzZXF1ZW5jZSI6Nzd9LCJzdG9yZV9pZCI6IjAxMjM0NTY3ODlhYmNkZWYwMTIzNDU2Nzg5YWJjZGVmIiwicmV2aXNpb24iOjMsImVuYWJsZWQiOnRydWUsInJlYWR5Ijp0cnVlLCJwZW5kaW5nIjowLCJ3YWl0aW5nX2Zvcl9jbG9jayI6MH0="} +{"header": {"topic": "schedule/due", "kind": "latest", "enc": "json", "type": "lamp_msgs/ScheduleDue", "source": "schedule", "boot": "5c4ed01e5c4ed01e", "seq": 78, "at_us": 77000000, "at_unix_ms": 1792130477000}, "payload_b64": "eyJzdGFtcCI6eyJjbG9ja19pZCI6InNjaGVkdWxlIiwiYXRfdXMiOjc3MDAwMDAwLCJzZXF1ZW5jZSI6Nzh9LCJzdG9yZV9pZCI6IjAxMjM0NTY3ODlhYmNkZWYwMTIzNDU2Nzg5YWJjZGVmIiwicmV2aXNpb24iOjMsImVuYWJsZWQiOnRydWUsInJlYWR5Ijp0cnVlLCJwZW5kaW5nIjowLCJ3YWl0aW5nX2Zvcl9jbG9jayI6MH0="} +{"header": {"topic": "schedule/due", "kind": "latest", "enc": "json", "type": "lamp_msgs/ScheduleDue", "source": "schedule", "boot": "5c4ed01e5c4ed01e", "seq": 79, "at_us": 78000000, "at_unix_ms": 1792130478000}, "payload_b64": "eyJzdGFtcCI6eyJjbG9ja19pZCI6InNjaGVkdWxlIiwiYXRfdXMiOjc4MDAwMDAwLCJzZXF1ZW5jZSI6Nzl9LCJzdG9yZV9pZCI6IjAxMjM0NTY3ODlhYmNkZWYwMTIzNDU2Nzg5YWJjZGVmIiwicmV2aXNpb24iOjMsImVuYWJsZWQiOnRydWUsInJlYWR5Ijp0cnVlLCJwZW5kaW5nIjowLCJ3YWl0aW5nX2Zvcl9jbG9jayI6MH0="} +{"header": {"topic": "schedule/due", "kind": "latest", "enc": "json", "type": "lamp_msgs/ScheduleDue", "source": "schedule", "boot": "5c4ed01e5c4ed01e", "seq": 80, "at_us": 79000000, "at_unix_ms": 1792130479000}, "payload_b64": "eyJzdGFtcCI6eyJjbG9ja19pZCI6InNjaGVkdWxlIiwiYXRfdXMiOjc5MDAwMDAwLCJzZXF1ZW5jZSI6ODB9LCJzdG9yZV9pZCI6IjAxMjM0NTY3ODlhYmNkZWYwMTIzNDU2Nzg5YWJjZGVmIiwicmV2aXNpb24iOjMsImVuYWJsZWQiOnRydWUsInJlYWR5Ijp0cnVlLCJwZW5kaW5nIjowLCJ3YWl0aW5nX2Zvcl9jbG9jayI6MH0="} +{"header": {"topic": "schedule/due", "kind": "latest", "enc": "json", "type": "lamp_msgs/ScheduleDue", "source": "schedule", "boot": "5c4ed01e5c4ed01e", "seq": 81, "at_us": 80000000, "at_unix_ms": 1792130480000}, "payload_b64": "eyJzdGFtcCI6eyJjbG9ja19pZCI6InNjaGVkdWxlIiwiYXRfdXMiOjgwMDAwMDAwLCJzZXF1ZW5jZSI6ODF9LCJzdG9yZV9pZCI6IjAxMjM0NTY3ODlhYmNkZWYwMTIzNDU2Nzg5YWJjZGVmIiwicmV2aXNpb24iOjMsImVuYWJsZWQiOnRydWUsInJlYWR5Ijp0cnVlLCJwZW5kaW5nIjowLCJ3YWl0aW5nX2Zvcl9jbG9jayI6MH0="} +{"header": {"topic": "schedule/due", "kind": "latest", "enc": "json", "type": "lamp_msgs/ScheduleDue", "source": "schedule", "boot": "5c4ed01e5c4ed01e", "seq": 82, "at_us": 81000000, "at_unix_ms": 1792130481000}, "payload_b64": "eyJzdGFtcCI6eyJjbG9ja19pZCI6InNjaGVkdWxlIiwiYXRfdXMiOjgxMDAwMDAwLCJzZXF1ZW5jZSI6ODJ9LCJzdG9yZV9pZCI6IjAxMjM0NTY3ODlhYmNkZWYwMTIzNDU2Nzg5YWJjZGVmIiwicmV2aXNpb24iOjMsImVuYWJsZWQiOnRydWUsInJlYWR5Ijp0cnVlLCJwZW5kaW5nIjowLCJ3YWl0aW5nX2Zvcl9jbG9jayI6MH0="} +{"header": {"topic": "schedule/due", "kind": "latest", "enc": "json", "type": "lamp_msgs/ScheduleDue", "source": "schedule", "boot": "5c4ed01e5c4ed01e", "seq": 83, "at_us": 82000000, "at_unix_ms": 1792130482000}, "payload_b64": "eyJzdGFtcCI6eyJjbG9ja19pZCI6InNjaGVkdWxlIiwiYXRfdXMiOjgyMDAwMDAwLCJzZXF1ZW5jZSI6ODN9LCJzdG9yZV9pZCI6IjAxMjM0NTY3ODlhYmNkZWYwMTIzNDU2Nzg5YWJjZGVmIiwicmV2aXNpb24iOjMsImVuYWJsZWQiOnRydWUsInJlYWR5Ijp0cnVlLCJwZW5kaW5nIjowLCJ3YWl0aW5nX2Zvcl9jbG9jayI6MH0="} +{"header": {"topic": "schedule/due", "kind": "latest", "enc": "json", "type": "lamp_msgs/ScheduleDue", "source": "schedule", "boot": "5c4ed01e5c4ed01e", "seq": 84, "at_us": 83000000, "at_unix_ms": 1792130483000}, "payload_b64": "eyJzdGFtcCI6eyJjbG9ja19pZCI6InNjaGVkdWxlIiwiYXRfdXMiOjgzMDAwMDAwLCJzZXF1ZW5jZSI6ODR9LCJzdG9yZV9pZCI6IjAxMjM0NTY3ODlhYmNkZWYwMTIzNDU2Nzg5YWJjZGVmIiwicmV2aXNpb24iOjMsImVuYWJsZWQiOnRydWUsInJlYWR5Ijp0cnVlLCJwZW5kaW5nIjowLCJ3YWl0aW5nX2Zvcl9jbG9jayI6MH0="} +{"header": {"topic": "schedule/due", "kind": "latest", "enc": "json", "type": "lamp_msgs/ScheduleDue", "source": "schedule", "boot": "5c4ed01e5c4ed01e", "seq": 85, "at_us": 84000000, "at_unix_ms": 1792130484000}, "payload_b64": "eyJzdGFtcCI6eyJjbG9ja19pZCI6InNjaGVkdWxlIiwiYXRfdXMiOjg0MDAwMDAwLCJzZXF1ZW5jZSI6ODV9LCJzdG9yZV9pZCI6IjAxMjM0NTY3ODlhYmNkZWYwMTIzNDU2Nzg5YWJjZGVmIiwicmV2aXNpb24iOjMsImVuYWJsZWQiOnRydWUsInJlYWR5Ijp0cnVlLCJwZW5kaW5nIjowLCJ3YWl0aW5nX2Zvcl9jbG9jayI6MH0="} +{"header": {"topic": "schedule/due", "kind": "latest", "enc": "json", "type": "lamp_msgs/ScheduleDue", "source": "schedule", "boot": "5c4ed01e5c4ed01e", "seq": 86, "at_us": 85000000, "at_unix_ms": 1792130485000}, "payload_b64": "eyJzdGFtcCI6eyJjbG9ja19pZCI6InNjaGVkdWxlIiwiYXRfdXMiOjg1MDAwMDAwLCJzZXF1ZW5jZSI6ODZ9LCJzdG9yZV9pZCI6IjAxMjM0NTY3ODlhYmNkZWYwMTIzNDU2Nzg5YWJjZGVmIiwicmV2aXNpb24iOjMsImVuYWJsZWQiOnRydWUsInJlYWR5Ijp0cnVlLCJwZW5kaW5nIjowLCJ3YWl0aW5nX2Zvcl9jbG9jayI6MH0="} +{"header": {"topic": "schedule/due", "kind": "latest", "enc": "json", "type": "lamp_msgs/ScheduleDue", "source": "schedule", "boot": "5c4ed01e5c4ed01e", "seq": 87, "at_us": 86000000, "at_unix_ms": 1792130486000}, "payload_b64": "eyJzdGFtcCI6eyJjbG9ja19pZCI6InNjaGVkdWxlIiwiYXRfdXMiOjg2MDAwMDAwLCJzZXF1ZW5jZSI6ODd9LCJzdG9yZV9pZCI6IjAxMjM0NTY3ODlhYmNkZWYwMTIzNDU2Nzg5YWJjZGVmIiwicmV2aXNpb24iOjMsImVuYWJsZWQiOnRydWUsInJlYWR5Ijp0cnVlLCJwZW5kaW5nIjowLCJ3YWl0aW5nX2Zvcl9jbG9jayI6MH0="} +{"header": {"topic": "schedule/due", "kind": "latest", "enc": "json", "type": "lamp_msgs/ScheduleDue", "source": "schedule", "boot": "5c4ed01e5c4ed01e", "seq": 88, "at_us": 87000000, "at_unix_ms": 1792130487000}, "payload_b64": "eyJzdGFtcCI6eyJjbG9ja19pZCI6InNjaGVkdWxlIiwiYXRfdXMiOjg3MDAwMDAwLCJzZXF1ZW5jZSI6ODh9LCJzdG9yZV9pZCI6IjAxMjM0NTY3ODlhYmNkZWYwMTIzNDU2Nzg5YWJjZGVmIiwicmV2aXNpb24iOjMsImVuYWJsZWQiOnRydWUsInJlYWR5Ijp0cnVlLCJwZW5kaW5nIjowLCJ3YWl0aW5nX2Zvcl9jbG9jayI6MH0="} +{"header": {"topic": "schedule/due", "kind": "latest", "enc": "json", "type": "lamp_msgs/ScheduleDue", "source": "schedule", "boot": "5c4ed01e5c4ed01e", "seq": 89, "at_us": 88000000, "at_unix_ms": 1792130488000}, "payload_b64": "eyJzdGFtcCI6eyJjbG9ja19pZCI6InNjaGVkdWxlIiwiYXRfdXMiOjg4MDAwMDAwLCJzZXF1ZW5jZSI6ODl9LCJzdG9yZV9pZCI6IjAxMjM0NTY3ODlhYmNkZWYwMTIzNDU2Nzg5YWJjZGVmIiwicmV2aXNpb24iOjMsImVuYWJsZWQiOnRydWUsInJlYWR5Ijp0cnVlLCJwZW5kaW5nIjowLCJ3YWl0aW5nX2Zvcl9jbG9jayI6MH0="} +{"header": {"topic": "schedule/due", "kind": "latest", "enc": "json", "type": "lamp_msgs/ScheduleDue", "source": "schedule", "boot": "5c4ed01e5c4ed01e", "seq": 90, "at_us": 89000000, "at_unix_ms": 1792130489000}, "payload_b64": "eyJzdGFtcCI6eyJjbG9ja19pZCI6InNjaGVkdWxlIiwiYXRfdXMiOjg5MDAwMDAwLCJzZXF1ZW5jZSI6OTB9LCJzdG9yZV9pZCI6IjAxMjM0NTY3ODlhYmNkZWYwMTIzNDU2Nzg5YWJjZGVmIiwicmV2aXNpb24iOjMsImVuYWJsZWQiOnRydWUsInJlYWR5Ijp0cnVlLCJwZW5kaW5nIjowLCJ3YWl0aW5nX2Zvcl9jbG9jayI6MH0="} +{"header": {"topic": "schedule/due", "kind": "latest", "enc": "json", "type": "lamp_msgs/ScheduleDue", "source": "schedule", "boot": "5c4ed01e5c4ed01e", "seq": 91, "at_us": 90000000, "at_unix_ms": 1792130490000}, "payload_b64": "eyJzdGFtcCI6eyJjbG9ja19pZCI6InNjaGVkdWxlIiwiYXRfdXMiOjkwMDAwMDAwLCJzZXF1ZW5jZSI6OTF9LCJzdG9yZV9pZCI6IjAxMjM0NTY3ODlhYmNkZWYwMTIzNDU2Nzg5YWJjZGVmIiwicmV2aXNpb24iOjMsImVuYWJsZWQiOnRydWUsInJlYWR5Ijp0cnVlLCJwZW5kaW5nIjowLCJ3YWl0aW5nX2Zvcl9jbG9jayI6MH0="} diff --git a/lampOS/skills/reminders/tests/due-during-a-talk-said-when-it-ends/voice_state.jsonl b/lampOS/skills/reminders/tests/due-during-a-talk-said-when-it-ends/voice_state.jsonl new file mode 100644 index 0000000000..417bb9c401 --- /dev/null +++ b/lampOS/skills/reminders/tests/due-during-a-talk-said-when-it-ends/voice_state.jsonl @@ -0,0 +1,93 @@ +{"topic": "voice/state", "declaration": {"type": "lamp_msgs/VoiceState", "version": 1, "kind": "latest"}} +{"header": {"topic": "voice/state", "kind": "latest", "enc": "json", "type": "lamp_msgs/VoiceState", "source": "voice", "boot": "a0a0a0a0a0a0a0a0", "seq": 1, "at_us": 0, "at_unix_ms": 1792130400000}, "payload_b64": "eyJzdGF0ZSI6InJlc3RpbmcifQ=="} +{"header": {"topic": "voice/state", "kind": "latest", "enc": "json", "type": "lamp_msgs/VoiceState", "source": "voice", "boot": "a0a0a0a0a0a0a0a0", "seq": 2, "at_us": 1000000, "at_unix_ms": 1792130401000}, "payload_b64": "eyJzdGF0ZSI6InJlc3RpbmcifQ=="} +{"header": {"topic": "voice/state", "kind": "latest", "enc": "json", "type": "lamp_msgs/VoiceState", "source": "voice", "boot": "a0a0a0a0a0a0a0a0", "seq": 3, "at_us": 2000000, "at_unix_ms": 1792130402000}, "payload_b64": "eyJzdGF0ZSI6InJlc3RpbmcifQ=="} +{"header": {"topic": "voice/state", "kind": "latest", "enc": "json", "type": "lamp_msgs/VoiceState", "source": "voice", "boot": "a0a0a0a0a0a0a0a0", "seq": 4, "at_us": 3000000, "at_unix_ms": 1792130403000}, "payload_b64": "eyJzdGF0ZSI6InJlc3RpbmcifQ=="} +{"header": {"topic": "voice/state", "kind": "latest", "enc": "json", "type": "lamp_msgs/VoiceState", "source": "voice", "boot": "a0a0a0a0a0a0a0a0", "seq": 5, "at_us": 4000000, "at_unix_ms": 1792130404000}, "payload_b64": "eyJzdGF0ZSI6InJlc3RpbmcifQ=="} +{"header": {"topic": "voice/state", "kind": "latest", "enc": "json", "type": "lamp_msgs/VoiceState", "source": "voice", "boot": "a0a0a0a0a0a0a0a0", "seq": 6, "at_us": 5000000, "at_unix_ms": 1792130405000}, "payload_b64": "eyJzdGF0ZSI6InJlc3RpbmcifQ=="} +{"header": {"topic": "voice/state", "kind": "latest", "enc": "json", "type": "lamp_msgs/VoiceState", "source": "voice", "boot": "a0a0a0a0a0a0a0a0", "seq": 7, "at_us": 6000000, "at_unix_ms": 1792130406000}, "payload_b64": "eyJzdGF0ZSI6InJlc3RpbmcifQ=="} +{"header": {"topic": "voice/state", "kind": "latest", "enc": "json", "type": "lamp_msgs/VoiceState", "source": "voice", "boot": "a0a0a0a0a0a0a0a0", "seq": 8, "at_us": 7000000, "at_unix_ms": 1792130407000}, "payload_b64": "eyJzdGF0ZSI6InJlc3RpbmcifQ=="} +{"header": {"topic": "voice/state", "kind": "latest", "enc": "json", "type": "lamp_msgs/VoiceState", "source": "voice", "boot": "a0a0a0a0a0a0a0a0", "seq": 9, "at_us": 8000000, "at_unix_ms": 1792130408000}, "payload_b64": "eyJzdGF0ZSI6InJlc3RpbmcifQ=="} +{"header": {"topic": "voice/state", "kind": "latest", "enc": "json", "type": "lamp_msgs/VoiceState", "source": "voice", "boot": "a0a0a0a0a0a0a0a0", "seq": 10, "at_us": 9000000, "at_unix_ms": 1792130409000}, "payload_b64": "eyJzdGF0ZSI6InJlc3RpbmcifQ=="} +{"header": {"topic": "voice/state", "kind": "latest", "enc": "json", "type": "lamp_msgs/VoiceState", "source": "voice", "boot": "a0a0a0a0a0a0a0a0", "seq": 11, "at_us": 10000000, "at_unix_ms": 1792130410000}, "payload_b64": "eyJzdGF0ZSI6InJlc3RpbmcifQ=="} +{"header": {"topic": "voice/state", "kind": "latest", "enc": "json", "type": "lamp_msgs/VoiceState", "source": "voice", "boot": "a0a0a0a0a0a0a0a0", "seq": 12, "at_us": 11000000, "at_unix_ms": 1792130411000}, "payload_b64": "eyJzdGF0ZSI6InJlc3RpbmcifQ=="} +{"header": {"topic": "voice/state", "kind": "latest", "enc": "json", "type": "lamp_msgs/VoiceState", "source": "voice", "boot": "a0a0a0a0a0a0a0a0", "seq": 13, "at_us": 12000000, "at_unix_ms": 1792130412000}, "payload_b64": "eyJzdGF0ZSI6InJlc3RpbmcifQ=="} +{"header": {"topic": "voice/state", "kind": "latest", "enc": "json", "type": "lamp_msgs/VoiceState", "source": "voice", "boot": "a0a0a0a0a0a0a0a0", "seq": 14, "at_us": 13000000, "at_unix_ms": 1792130413000}, "payload_b64": "eyJzdGF0ZSI6InJlc3RpbmcifQ=="} +{"header": {"topic": "voice/state", "kind": "latest", "enc": "json", "type": "lamp_msgs/VoiceState", "source": "voice", "boot": "a0a0a0a0a0a0a0a0", "seq": 15, "at_us": 14000000, "at_unix_ms": 1792130414000}, "payload_b64": "eyJzdGF0ZSI6InJlc3RpbmcifQ=="} +{"header": {"topic": "voice/state", "kind": "latest", "enc": "json", "type": "lamp_msgs/VoiceState", "source": "voice", "boot": "a0a0a0a0a0a0a0a0", "seq": 16, "at_us": 15000000, "at_unix_ms": 1792130415000}, "payload_b64": "eyJzdGF0ZSI6InJlc3RpbmcifQ=="} +{"header": {"topic": "voice/state", "kind": "latest", "enc": "json", "type": "lamp_msgs/VoiceState", "source": "voice", "boot": "a0a0a0a0a0a0a0a0", "seq": 17, "at_us": 16000000, "at_unix_ms": 1792130416000}, "payload_b64": "eyJzdGF0ZSI6InJlc3RpbmcifQ=="} +{"header": {"topic": "voice/state", "kind": "latest", "enc": "json", "type": "lamp_msgs/VoiceState", "source": "voice", "boot": "a0a0a0a0a0a0a0a0", "seq": 18, "at_us": 17000000, "at_unix_ms": 1792130417000}, "payload_b64": "eyJzdGF0ZSI6InJlc3RpbmcifQ=="} +{"header": {"topic": "voice/state", "kind": "latest", "enc": "json", "type": "lamp_msgs/VoiceState", "source": "voice", "boot": "a0a0a0a0a0a0a0a0", "seq": 19, "at_us": 18000000, "at_unix_ms": 1792130418000}, "payload_b64": "eyJzdGF0ZSI6InJlc3RpbmcifQ=="} +{"header": {"topic": "voice/state", "kind": "latest", "enc": "json", "type": "lamp_msgs/VoiceState", "source": "voice", "boot": "a0a0a0a0a0a0a0a0", "seq": 20, "at_us": 19000000, "at_unix_ms": 1792130419000}, "payload_b64": "eyJzdGF0ZSI6InJlc3RpbmcifQ=="} +{"header": {"topic": "voice/state", "kind": "latest", "enc": "json", "type": "lamp_msgs/VoiceState", "source": "voice", "boot": "a0a0a0a0a0a0a0a0", "seq": 21, "at_us": 20000000, "at_unix_ms": 1792130420000}, "payload_b64": "eyJzdGF0ZSI6Imxpc3RlbmluZyJ9"} +{"header": {"topic": "voice/state", "kind": "latest", "enc": "json", "type": "lamp_msgs/VoiceState", "source": "voice", "boot": "a0a0a0a0a0a0a0a0", "seq": 22, "at_us": 21000000, "at_unix_ms": 1792130421000}, "payload_b64": "eyJzdGF0ZSI6Imxpc3RlbmluZyJ9"} +{"header": {"topic": "voice/state", "kind": "latest", "enc": "json", "type": "lamp_msgs/VoiceState", "source": "voice", "boot": "a0a0a0a0a0a0a0a0", "seq": 23, "at_us": 22000000, "at_unix_ms": 1792130422000}, "payload_b64": "eyJzdGF0ZSI6Imxpc3RlbmluZyJ9"} +{"header": {"topic": "voice/state", "kind": "latest", "enc": "json", "type": "lamp_msgs/VoiceState", "source": "voice", "boot": "a0a0a0a0a0a0a0a0", "seq": 24, "at_us": 23000000, "at_unix_ms": 1792130423000}, "payload_b64": "eyJzdGF0ZSI6Imxpc3RlbmluZyJ9"} +{"header": {"topic": "voice/state", "kind": "latest", "enc": "json", "type": "lamp_msgs/VoiceState", "source": "voice", "boot": "a0a0a0a0a0a0a0a0", "seq": 25, "at_us": 24000000, "at_unix_ms": 1792130424000}, "payload_b64": "eyJzdGF0ZSI6InRoaW5raW5nIn0="} +{"header": {"topic": "voice/state", "kind": "latest", "enc": "json", "type": "lamp_msgs/VoiceState", "source": "voice", "boot": "a0a0a0a0a0a0a0a0", "seq": 26, "at_us": 25000000, "at_unix_ms": 1792130425000}, "payload_b64": "eyJzdGF0ZSI6InNwZWFraW5nIn0="} +{"header": {"topic": "voice/state", "kind": "latest", "enc": "json", "type": "lamp_msgs/VoiceState", "source": "voice", "boot": "a0a0a0a0a0a0a0a0", "seq": 27, "at_us": 26000000, "at_unix_ms": 1792130426000}, "payload_b64": "eyJzdGF0ZSI6InNwZWFraW5nIn0="} +{"header": {"topic": "voice/state", "kind": "latest", "enc": "json", "type": "lamp_msgs/VoiceState", "source": "voice", "boot": "a0a0a0a0a0a0a0a0", "seq": 28, "at_us": 27000000, "at_unix_ms": 1792130427000}, "payload_b64": "eyJzdGF0ZSI6InNwZWFraW5nIn0="} +{"header": {"topic": "voice/state", "kind": "latest", "enc": "json", "type": "lamp_msgs/VoiceState", "source": "voice", "boot": "a0a0a0a0a0a0a0a0", "seq": 29, "at_us": 28000000, "at_unix_ms": 1792130428000}, "payload_b64": "eyJzdGF0ZSI6InNwZWFraW5nIn0="} +{"header": {"topic": "voice/state", "kind": "latest", "enc": "json", "type": "lamp_msgs/VoiceState", "source": "voice", "boot": "a0a0a0a0a0a0a0a0", "seq": 30, "at_us": 29000000, "at_unix_ms": 1792130429000}, "payload_b64": "eyJzdGF0ZSI6InNwZWFraW5nIn0="} +{"header": {"topic": "voice/state", "kind": "latest", "enc": "json", "type": "lamp_msgs/VoiceState", "source": "voice", "boot": "a0a0a0a0a0a0a0a0", "seq": 31, "at_us": 30000000, "at_unix_ms": 1792130430000}, "payload_b64": "eyJzdGF0ZSI6InNwZWFraW5nIn0="} +{"header": {"topic": "voice/state", "kind": "latest", "enc": "json", "type": "lamp_msgs/VoiceState", "source": "voice", "boot": "a0a0a0a0a0a0a0a0", "seq": 32, "at_us": 31000000, "at_unix_ms": 1792130431000}, "payload_b64": "eyJzdGF0ZSI6InNwZWFraW5nIn0="} +{"header": {"topic": "voice/state", "kind": "latest", "enc": "json", "type": "lamp_msgs/VoiceState", "source": "voice", "boot": "a0a0a0a0a0a0a0a0", "seq": 33, "at_us": 32000000, "at_unix_ms": 1792130432000}, "payload_b64": "eyJzdGF0ZSI6InNwZWFraW5nIn0="} +{"header": {"topic": "voice/state", "kind": "latest", "enc": "json", "type": "lamp_msgs/VoiceState", "source": "voice", "boot": "a0a0a0a0a0a0a0a0", "seq": 34, "at_us": 33000000, "at_unix_ms": 1792130433000}, "payload_b64": "eyJzdGF0ZSI6Imxpc3RlbmluZyJ9"} +{"header": {"topic": "voice/state", "kind": "latest", "enc": "json", "type": "lamp_msgs/VoiceState", "source": "voice", "boot": "a0a0a0a0a0a0a0a0", "seq": 35, "at_us": 34000000, "at_unix_ms": 1792130434000}, "payload_b64": "eyJzdGF0ZSI6Imxpc3RlbmluZyJ9"} +{"header": {"topic": "voice/state", "kind": "latest", "enc": "json", "type": "lamp_msgs/VoiceState", "source": "voice", "boot": "a0a0a0a0a0a0a0a0", "seq": 36, "at_us": 35000000, "at_unix_ms": 1792130435000}, "payload_b64": "eyJzdGF0ZSI6Imxpc3RlbmluZyJ9"} +{"header": {"topic": "voice/state", "kind": "latest", "enc": "json", "type": "lamp_msgs/VoiceState", "source": "voice", "boot": "a0a0a0a0a0a0a0a0", "seq": 37, "at_us": 36000000, "at_unix_ms": 1792130436000}, "payload_b64": "eyJzdGF0ZSI6Imxpc3RlbmluZyJ9"} +{"header": {"topic": "voice/state", "kind": "latest", "enc": "json", "type": "lamp_msgs/VoiceState", "source": "voice", "boot": "a0a0a0a0a0a0a0a0", "seq": 38, "at_us": 37000000, "at_unix_ms": 1792130437000}, "payload_b64": "eyJzdGF0ZSI6Imxpc3RlbmluZyJ9"} +{"header": {"topic": "voice/state", "kind": "latest", "enc": "json", "type": "lamp_msgs/VoiceState", "source": "voice", "boot": "a0a0a0a0a0a0a0a0", "seq": 39, "at_us": 38000000, "at_unix_ms": 1792130438000}, "payload_b64": "eyJzdGF0ZSI6InRoaW5raW5nIn0="} +{"header": {"topic": "voice/state", "kind": "latest", "enc": "json", "type": "lamp_msgs/VoiceState", "source": "voice", "boot": "a0a0a0a0a0a0a0a0", "seq": 40, "at_us": 39000000, "at_unix_ms": 1792130439000}, "payload_b64": "eyJzdGF0ZSI6InNwZWFraW5nIn0="} +{"header": {"topic": "voice/state", "kind": "latest", "enc": "json", "type": "lamp_msgs/VoiceState", "source": "voice", "boot": "a0a0a0a0a0a0a0a0", "seq": 41, "at_us": 40000000, "at_unix_ms": 1792130440000}, "payload_b64": "eyJzdGF0ZSI6InNwZWFraW5nIn0="} +{"header": {"topic": "voice/state", "kind": "latest", "enc": "json", "type": "lamp_msgs/VoiceState", "source": "voice", "boot": "a0a0a0a0a0a0a0a0", "seq": 42, "at_us": 41000000, "at_unix_ms": 1792130441000}, "payload_b64": "eyJzdGF0ZSI6InNwZWFraW5nIn0="} +{"header": {"topic": "voice/state", "kind": "latest", "enc": "json", "type": "lamp_msgs/VoiceState", "source": "voice", "boot": "a0a0a0a0a0a0a0a0", "seq": 43, "at_us": 42000000, "at_unix_ms": 1792130442000}, "payload_b64": "eyJzdGF0ZSI6InNwZWFraW5nIn0="} +{"header": {"topic": "voice/state", "kind": "latest", "enc": "json", "type": "lamp_msgs/VoiceState", "source": "voice", "boot": "a0a0a0a0a0a0a0a0", "seq": 44, "at_us": 43000000, "at_unix_ms": 1792130443000}, "payload_b64": "eyJzdGF0ZSI6InNwZWFraW5nIn0="} +{"header": {"topic": "voice/state", "kind": "latest", "enc": "json", "type": "lamp_msgs/VoiceState", "source": "voice", "boot": "a0a0a0a0a0a0a0a0", "seq": 45, "at_us": 44000000, "at_unix_ms": 1792130444000}, "payload_b64": "eyJzdGF0ZSI6InNwZWFraW5nIn0="} +{"header": {"topic": "voice/state", "kind": "latest", "enc": "json", "type": "lamp_msgs/VoiceState", "source": "voice", "boot": "a0a0a0a0a0a0a0a0", "seq": 46, "at_us": 45000000, "at_unix_ms": 1792130445000}, "payload_b64": "eyJzdGF0ZSI6InNwZWFraW5nIn0="} +{"header": {"topic": "voice/state", "kind": "latest", "enc": "json", "type": "lamp_msgs/VoiceState", "source": "voice", "boot": "a0a0a0a0a0a0a0a0", "seq": 47, "at_us": 46000000, "at_unix_ms": 1792130446000}, "payload_b64": "eyJzdGF0ZSI6InNwZWFraW5nIn0="} +{"header": {"topic": "voice/state", "kind": "latest", "enc": "json", "type": "lamp_msgs/VoiceState", "source": "voice", "boot": "a0a0a0a0a0a0a0a0", "seq": 48, "at_us": 47000000, "at_unix_ms": 1792130447000}, "payload_b64": "eyJzdGF0ZSI6Imxpc3RlbmluZyJ9"} +{"header": {"topic": "voice/state", "kind": "latest", "enc": "json", "type": "lamp_msgs/VoiceState", "source": "voice", "boot": "a0a0a0a0a0a0a0a0", "seq": 49, "at_us": 48000000, "at_unix_ms": 1792130448000}, "payload_b64": "eyJzdGF0ZSI6Imxpc3RlbmluZyJ9"} +{"header": {"topic": "voice/state", "kind": "latest", "enc": "json", "type": "lamp_msgs/VoiceState", "source": "voice", "boot": "a0a0a0a0a0a0a0a0", "seq": 50, "at_us": 49000000, "at_unix_ms": 1792130449000}, "payload_b64": "eyJzdGF0ZSI6Imxpc3RlbmluZyJ9"} +{"header": {"topic": "voice/state", "kind": "latest", "enc": "json", "type": "lamp_msgs/VoiceState", "source": "voice", "boot": "a0a0a0a0a0a0a0a0", "seq": 51, "at_us": 50000000, "at_unix_ms": 1792130450000}, "payload_b64": "eyJzdGF0ZSI6Imxpc3RlbmluZyJ9"} +{"header": {"topic": "voice/state", "kind": "latest", "enc": "json", "type": "lamp_msgs/VoiceState", "source": "voice", "boot": "a0a0a0a0a0a0a0a0", "seq": 52, "at_us": 51000000, "at_unix_ms": 1792130451000}, "payload_b64": "eyJzdGF0ZSI6Imxpc3RlbmluZyJ9"} +{"header": {"topic": "voice/state", "kind": "latest", "enc": "json", "type": "lamp_msgs/VoiceState", "source": "voice", "boot": "a0a0a0a0a0a0a0a0", "seq": 53, "at_us": 52000000, "at_unix_ms": 1792130452000}, "payload_b64": "eyJzdGF0ZSI6InRoaW5raW5nIn0="} +{"header": {"topic": "voice/state", "kind": "latest", "enc": "json", "type": "lamp_msgs/VoiceState", "source": "voice", "boot": "a0a0a0a0a0a0a0a0", "seq": 54, "at_us": 53000000, "at_unix_ms": 1792130453000}, "payload_b64": "eyJzdGF0ZSI6InNwZWFraW5nIn0="} +{"header": {"topic": "voice/state", "kind": "latest", "enc": "json", "type": "lamp_msgs/VoiceState", "source": "voice", "boot": "a0a0a0a0a0a0a0a0", "seq": 55, "at_us": 54000000, "at_unix_ms": 1792130454000}, "payload_b64": "eyJzdGF0ZSI6InNwZWFraW5nIn0="} +{"header": {"topic": "voice/state", "kind": "latest", "enc": "json", "type": "lamp_msgs/VoiceState", "source": "voice", "boot": "a0a0a0a0a0a0a0a0", "seq": 56, "at_us": 55000000, "at_unix_ms": 1792130455000}, "payload_b64": "eyJzdGF0ZSI6InNwZWFraW5nIn0="} +{"header": {"topic": "voice/state", "kind": "latest", "enc": "json", "type": "lamp_msgs/VoiceState", "source": "voice", "boot": "a0a0a0a0a0a0a0a0", "seq": 57, "at_us": 56000000, "at_unix_ms": 1792130456000}, "payload_b64": "eyJzdGF0ZSI6InNwZWFraW5nIn0="} +{"header": {"topic": "voice/state", "kind": "latest", "enc": "json", "type": "lamp_msgs/VoiceState", "source": "voice", "boot": "a0a0a0a0a0a0a0a0", "seq": 58, "at_us": 57000000, "at_unix_ms": 1792130457000}, "payload_b64": "eyJzdGF0ZSI6InNwZWFraW5nIn0="} +{"header": {"topic": "voice/state", "kind": "latest", "enc": "json", "type": "lamp_msgs/VoiceState", "source": "voice", "boot": "a0a0a0a0a0a0a0a0", "seq": 59, "at_us": 58000000, "at_unix_ms": 1792130458000}, "payload_b64": "eyJzdGF0ZSI6InNwZWFraW5nIn0="} +{"header": {"topic": "voice/state", "kind": "latest", "enc": "json", "type": "lamp_msgs/VoiceState", "source": "voice", "boot": "a0a0a0a0a0a0a0a0", "seq": 60, "at_us": 59000000, "at_unix_ms": 1792130459000}, "payload_b64": "eyJzdGF0ZSI6InNwZWFraW5nIn0="} +{"header": {"topic": "voice/state", "kind": "latest", "enc": "json", "type": "lamp_msgs/VoiceState", "source": "voice", "boot": "a0a0a0a0a0a0a0a0", "seq": 61, "at_us": 60000000, "at_unix_ms": 1792130460000}, "payload_b64": "eyJzdGF0ZSI6InNwZWFraW5nIn0="} +{"header": {"topic": "voice/state", "kind": "latest", "enc": "json", "type": "lamp_msgs/VoiceState", "source": "voice", "boot": "a0a0a0a0a0a0a0a0", "seq": 62, "at_us": 60500000, "at_unix_ms": 1792130460500}, "payload_b64": "eyJzdGF0ZSI6InJlc3RpbmcifQ=="} +{"header": {"topic": "voice/state", "kind": "latest", "enc": "json", "type": "lamp_msgs/VoiceState", "source": "voice", "boot": "a0a0a0a0a0a0a0a0", "seq": 63, "at_us": 61000000, "at_unix_ms": 1792130461000}, "payload_b64": "eyJzdGF0ZSI6InJlc3RpbmcifQ=="} +{"header": {"topic": "voice/state", "kind": "latest", "enc": "json", "type": "lamp_msgs/VoiceState", "source": "voice", "boot": "a0a0a0a0a0a0a0a0", "seq": 64, "at_us": 62000000, "at_unix_ms": 1792130462000}, "payload_b64": "eyJzdGF0ZSI6InJlc3RpbmcifQ=="} +{"header": {"topic": "voice/state", "kind": "latest", "enc": "json", "type": "lamp_msgs/VoiceState", "source": "voice", "boot": "a0a0a0a0a0a0a0a0", "seq": 65, "at_us": 63000000, "at_unix_ms": 1792130463000}, "payload_b64": "eyJzdGF0ZSI6InJlc3RpbmcifQ=="} +{"header": {"topic": "voice/state", "kind": "latest", "enc": "json", "type": "lamp_msgs/VoiceState", "source": "voice", "boot": "a0a0a0a0a0a0a0a0", "seq": 66, "at_us": 64000000, "at_unix_ms": 1792130464000}, "payload_b64": "eyJzdGF0ZSI6InJlc3RpbmcifQ=="} +{"header": {"topic": "voice/state", "kind": "latest", "enc": "json", "type": "lamp_msgs/VoiceState", "source": "voice", "boot": "a0a0a0a0a0a0a0a0", "seq": 67, "at_us": 65000000, "at_unix_ms": 1792130465000}, "payload_b64": "eyJzdGF0ZSI6InJlc3RpbmcifQ=="} +{"header": {"topic": "voice/state", "kind": "latest", "enc": "json", "type": "lamp_msgs/VoiceState", "source": "voice", "boot": "a0a0a0a0a0a0a0a0", "seq": 68, "at_us": 66000000, "at_unix_ms": 1792130466000}, "payload_b64": "eyJzdGF0ZSI6InJlc3RpbmcifQ=="} +{"header": {"topic": "voice/state", "kind": "latest", "enc": "json", "type": "lamp_msgs/VoiceState", "source": "voice", "boot": "a0a0a0a0a0a0a0a0", "seq": 69, "at_us": 67000000, "at_unix_ms": 1792130467000}, "payload_b64": "eyJzdGF0ZSI6InJlc3RpbmcifQ=="} +{"header": {"topic": "voice/state", "kind": "latest", "enc": "json", "type": "lamp_msgs/VoiceState", "source": "voice", "boot": "a0a0a0a0a0a0a0a0", "seq": 70, "at_us": 68000000, "at_unix_ms": 1792130468000}, "payload_b64": "eyJzdGF0ZSI6InJlc3RpbmcifQ=="} +{"header": {"topic": "voice/state", "kind": "latest", "enc": "json", "type": "lamp_msgs/VoiceState", "source": "voice", "boot": "a0a0a0a0a0a0a0a0", "seq": 71, "at_us": 69000000, "at_unix_ms": 1792130469000}, "payload_b64": "eyJzdGF0ZSI6InJlc3RpbmcifQ=="} +{"header": {"topic": "voice/state", "kind": "latest", "enc": "json", "type": "lamp_msgs/VoiceState", "source": "voice", "boot": "a0a0a0a0a0a0a0a0", "seq": 72, "at_us": 70000000, "at_unix_ms": 1792130470000}, "payload_b64": "eyJzdGF0ZSI6InJlc3RpbmcifQ=="} +{"header": {"topic": "voice/state", "kind": "latest", "enc": "json", "type": "lamp_msgs/VoiceState", "source": "voice", "boot": "a0a0a0a0a0a0a0a0", "seq": 73, "at_us": 71000000, "at_unix_ms": 1792130471000}, "payload_b64": "eyJzdGF0ZSI6InJlc3RpbmcifQ=="} +{"header": {"topic": "voice/state", "kind": "latest", "enc": "json", "type": "lamp_msgs/VoiceState", "source": "voice", "boot": "a0a0a0a0a0a0a0a0", "seq": 74, "at_us": 72000000, "at_unix_ms": 1792130472000}, "payload_b64": "eyJzdGF0ZSI6InJlc3RpbmcifQ=="} +{"header": {"topic": "voice/state", "kind": "latest", "enc": "json", "type": "lamp_msgs/VoiceState", "source": "voice", "boot": "a0a0a0a0a0a0a0a0", "seq": 75, "at_us": 73000000, "at_unix_ms": 1792130473000}, "payload_b64": "eyJzdGF0ZSI6InJlc3RpbmcifQ=="} +{"header": {"topic": "voice/state", "kind": "latest", "enc": "json", "type": "lamp_msgs/VoiceState", "source": "voice", "boot": "a0a0a0a0a0a0a0a0", "seq": 76, "at_us": 74000000, "at_unix_ms": 1792130474000}, "payload_b64": "eyJzdGF0ZSI6InJlc3RpbmcifQ=="} +{"header": {"topic": "voice/state", "kind": "latest", "enc": "json", "type": "lamp_msgs/VoiceState", "source": "voice", "boot": "a0a0a0a0a0a0a0a0", "seq": 77, "at_us": 75000000, "at_unix_ms": 1792130475000}, "payload_b64": "eyJzdGF0ZSI6InJlc3RpbmcifQ=="} +{"header": {"topic": "voice/state", "kind": "latest", "enc": "json", "type": "lamp_msgs/VoiceState", "source": "voice", "boot": "a0a0a0a0a0a0a0a0", "seq": 78, "at_us": 76000000, "at_unix_ms": 1792130476000}, "payload_b64": "eyJzdGF0ZSI6InJlc3RpbmcifQ=="} +{"header": {"topic": "voice/state", "kind": "latest", "enc": "json", "type": "lamp_msgs/VoiceState", "source": "voice", "boot": "a0a0a0a0a0a0a0a0", "seq": 79, "at_us": 77000000, "at_unix_ms": 1792130477000}, "payload_b64": "eyJzdGF0ZSI6InJlc3RpbmcifQ=="} +{"header": {"topic": "voice/state", "kind": "latest", "enc": "json", "type": "lamp_msgs/VoiceState", "source": "voice", "boot": "a0a0a0a0a0a0a0a0", "seq": 80, "at_us": 78000000, "at_unix_ms": 1792130478000}, "payload_b64": "eyJzdGF0ZSI6InJlc3RpbmcifQ=="} +{"header": {"topic": "voice/state", "kind": "latest", "enc": "json", "type": "lamp_msgs/VoiceState", "source": "voice", "boot": "a0a0a0a0a0a0a0a0", "seq": 81, "at_us": 79000000, "at_unix_ms": 1792130479000}, "payload_b64": "eyJzdGF0ZSI6InJlc3RpbmcifQ=="} +{"header": {"topic": "voice/state", "kind": "latest", "enc": "json", "type": "lamp_msgs/VoiceState", "source": "voice", "boot": "a0a0a0a0a0a0a0a0", "seq": 82, "at_us": 80000000, "at_unix_ms": 1792130480000}, "payload_b64": "eyJzdGF0ZSI6InJlc3RpbmcifQ=="} +{"header": {"topic": "voice/state", "kind": "latest", "enc": "json", "type": "lamp_msgs/VoiceState", "source": "voice", "boot": "a0a0a0a0a0a0a0a0", "seq": 83, "at_us": 81000000, "at_unix_ms": 1792130481000}, "payload_b64": "eyJzdGF0ZSI6InJlc3RpbmcifQ=="} +{"header": {"topic": "voice/state", "kind": "latest", "enc": "json", "type": "lamp_msgs/VoiceState", "source": "voice", "boot": "a0a0a0a0a0a0a0a0", "seq": 84, "at_us": 82000000, "at_unix_ms": 1792130482000}, "payload_b64": "eyJzdGF0ZSI6InJlc3RpbmcifQ=="} +{"header": {"topic": "voice/state", "kind": "latest", "enc": "json", "type": "lamp_msgs/VoiceState", "source": "voice", "boot": "a0a0a0a0a0a0a0a0", "seq": 85, "at_us": 83000000, "at_unix_ms": 1792130483000}, "payload_b64": "eyJzdGF0ZSI6InJlc3RpbmcifQ=="} +{"header": {"topic": "voice/state", "kind": "latest", "enc": "json", "type": "lamp_msgs/VoiceState", "source": "voice", "boot": "a0a0a0a0a0a0a0a0", "seq": 86, "at_us": 84000000, "at_unix_ms": 1792130484000}, "payload_b64": "eyJzdGF0ZSI6InJlc3RpbmcifQ=="} +{"header": {"topic": "voice/state", "kind": "latest", "enc": "json", "type": "lamp_msgs/VoiceState", "source": "voice", "boot": "a0a0a0a0a0a0a0a0", "seq": 87, "at_us": 85000000, "at_unix_ms": 1792130485000}, "payload_b64": "eyJzdGF0ZSI6InJlc3RpbmcifQ=="} +{"header": {"topic": "voice/state", "kind": "latest", "enc": "json", "type": "lamp_msgs/VoiceState", "source": "voice", "boot": "a0a0a0a0a0a0a0a0", "seq": 88, "at_us": 86000000, "at_unix_ms": 1792130486000}, "payload_b64": "eyJzdGF0ZSI6InJlc3RpbmcifQ=="} +{"header": {"topic": "voice/state", "kind": "latest", "enc": "json", "type": "lamp_msgs/VoiceState", "source": "voice", "boot": "a0a0a0a0a0a0a0a0", "seq": 89, "at_us": 87000000, "at_unix_ms": 1792130487000}, "payload_b64": "eyJzdGF0ZSI6InJlc3RpbmcifQ=="} +{"header": {"topic": "voice/state", "kind": "latest", "enc": "json", "type": "lamp_msgs/VoiceState", "source": "voice", "boot": "a0a0a0a0a0a0a0a0", "seq": 90, "at_us": 88000000, "at_unix_ms": 1792130488000}, "payload_b64": "eyJzdGF0ZSI6InJlc3RpbmcifQ=="} +{"header": {"topic": "voice/state", "kind": "latest", "enc": "json", "type": "lamp_msgs/VoiceState", "source": "voice", "boot": "a0a0a0a0a0a0a0a0", "seq": 91, "at_us": 89000000, "at_unix_ms": 1792130489000}, "payload_b64": "eyJzdGF0ZSI6InJlc3RpbmcifQ=="} +{"header": {"topic": "voice/state", "kind": "latest", "enc": "json", "type": "lamp_msgs/VoiceState", "source": "voice", "boot": "a0a0a0a0a0a0a0a0", "seq": 92, "at_us": 90000000, "at_unix_ms": 1792130490000}, "payload_b64": "eyJzdGF0ZSI6InJlc3RpbmcifQ=="} diff --git a/lampOS/skills/reminders/tests/due-while-they-are-at-the-desk/voice_state.jsonl b/lampOS/skills/reminders/tests/due-while-they-are-at-the-desk/voice_state.jsonl new file mode 100644 index 0000000000..a4e668965f --- /dev/null +++ b/lampOS/skills/reminders/tests/due-while-they-are-at-the-desk/voice_state.jsonl @@ -0,0 +1,2 @@ +{"topic": "voice/state", "declaration": {"type": "lamp_msgs/VoiceState", "version": 1, "kind": "latest"}} +{"header": {"topic": "voice/state", "kind": "latest", "enc": "json", "type": "lamp_msgs/VoiceState", "source": "voice", "boot": "a0a0a0a0a0a0a0a0", "seq": 1, "at_us": 0, "at_unix_ms": 1792130400000}, "payload_b64": "eyJzdGF0ZSI6InJlc3RpbmcifQ=="} diff --git a/lampOS/skills/reminders/tests/each-reminder-once/voice_state.jsonl b/lampOS/skills/reminders/tests/each-reminder-once/voice_state.jsonl new file mode 100644 index 0000000000..a4e668965f --- /dev/null +++ b/lampOS/skills/reminders/tests/each-reminder-once/voice_state.jsonl @@ -0,0 +1,2 @@ +{"topic": "voice/state", "declaration": {"type": "lamp_msgs/VoiceState", "version": 1, "kind": "latest"}} +{"header": {"topic": "voice/state", "kind": "latest", "enc": "json", "type": "lamp_msgs/VoiceState", "source": "voice", "boot": "a0a0a0a0a0a0a0a0", "seq": 1, "at_us": 0, "at_unix_ms": 1792130400000}, "payload_b64": "eyJzdGF0ZSI6InJlc3RpbmcifQ=="} diff --git a/lampOS/skills/reminders/tests/quiet-while-schedule-cannot-serve-it/expect.toml b/lampOS/skills/reminders/tests/quiet-while-schedule-cannot-serve-it/expect.toml index f45fca5027..4a62bbf833 100644 --- a/lampOS/skills/reminders/tests/quiet-while-schedule-cannot-serve-it/expect.toml +++ b/lampOS/skills/reminders/tests/quiet-while-schedule-cannot-serve-it/expect.toml @@ -1,4 +1,14 @@ # A reminder is counted due at 20 s while Schedule is not ready to serve it: -# nothing. Ready at 40 s with it still due: it held before, so it has not -# just appeared, and the voice says it by itself once it is offered. -none = true +# nothing. Ready at 40 s with it still due: the chance kept since 20 s +# (wait, the reviewer's P2 on #1667) is taken then, as the voice is offered +# it, so the lamp looks at them as it is said. + +[[expect]] +at = "40s" +call = "body.gaze" +args = { look_at = "face", x = 0.0, y = -0.25, size = 0.25 } + +[[expect]] +at = "40s" +call = "say" +args = { from = "schedule" } diff --git a/lampOS/skills/reminders/tests/quiet-while-schedule-cannot-serve-it/voice_state.jsonl b/lampOS/skills/reminders/tests/quiet-while-schedule-cannot-serve-it/voice_state.jsonl new file mode 100644 index 0000000000..a4e668965f --- /dev/null +++ b/lampOS/skills/reminders/tests/quiet-while-schedule-cannot-serve-it/voice_state.jsonl @@ -0,0 +1,2 @@ +{"topic": "voice/state", "declaration": {"type": "lamp_msgs/VoiceState", "version": 1, "kind": "latest"}} +{"header": {"topic": "voice/state", "kind": "latest", "enc": "json", "type": "lamp_msgs/VoiceState", "source": "voice", "boot": "a0a0a0a0a0a0a0a0", "seq": 1, "at_us": 0, "at_unix_ms": 1792130400000}, "payload_b64": "eyJzdGF0ZSI6InJlc3RpbmcifQ=="} diff --git a/lampOS/skills/task-news/SKILL.md b/lampOS/skills/task-news/SKILL.md index 6978d54a54..fce9446cea 100644 --- a/lampOS/skills/task-news/SKILL.md +++ b/lampOS/skills/task-news/SKILL.md @@ -31,6 +31,14 @@ agent's own words stay with the tasks part and the voice, never on the bus. Asked about it, the lamp says it tells you when work you handed to Claude Code or Codex comes back, once and briefly, and that it can be switched off. -Off on a lamp until it has been tried there (5 of 5 on lamp-4ace with the -voice's own task news switched off); until then the voice's built-in news -(`crates/voice/src/task_news.rs`) tells it. +On every lamp the product installs, with the voice's `LAMP_TASK_NEWS=skill`: +the voice waits for this skill's ask until 10 s after it had the news, then +tells it itself, so each piece is told once, with the sway or the droop when +the skill asked. This skill asks the moment the work settles (an item's run +cannot wait), and its ask is gone 20 s later: news that settled while +nobody was there, or long before a talk ended, is told by the voice when it +can be, at once, without the sway. +News of work that stopped, or that asks something, has no trigger here: the +voice tells it after those 10 s. The voice's own timing of the news +(`crates/voice/src/task_news.rs`) is taken out once this has done 5 of 5 on +lamp-4ace. diff --git a/lampOS/skills/task-news/skill.toml b/lampOS/skills/task-news/skill.toml index 3b35fcaec0..63c23ef309 100644 --- a/lampOS/skills/task-news/skill.toml +++ b/lampOS/skills/task-news/skill.toml @@ -3,9 +3,11 @@ # at the next desk would (#1021, from #939). Written to lamp-skills' schema # (docs/skills.md, "The schema, as lamp-skills reads it"). # -# OFF ON A LAMP until it has done 5 of 5 on lamp-4ace with the voice's own -# task news (crates/voice/src/task_news.rs) switched off for the trial: -# with both on, a done would be told twice. +# On every product lamp (lamp-update's install), with the voice's +# LAMP_TASK_NEWS=skill: the voice keeps the news and waits for this skill's +# ask until 10 s after it had the news, then tells it itself, so each task +# is told once whether the skill asks or not. 5 of 5 on lamp-4ace come before the voice's own +# timing of it (crates/voice/src/task_news.rs) is taken out. needs = ["eyes", "voice", "body", "light"] # tasks/status keeps, for two minutes, each task whose agent settled, in