iobroker.assistant 1.4.8 → 1.4.9

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/build/main.js CHANGED
@@ -996,6 +996,11 @@ class Assistant extends adapter_core_1.Adapter {
996
996
  const roomName = (room || '').replace(/^(enum\.rooms\.|system\.rooms\.)/, '');
997
997
  return (roomName || device).replace(/[^\w-]/g, '_') || 'unknown';
998
998
  }
999
+ /** The id under `satellites.*` of the satellite a request came from, or '' when it has no states there (Wyoming). */
1000
+ knownSatelliteId(device, room) {
1001
+ const id = this.satelliteStateId(device, room);
1002
+ return this.satStatesEnsured.has(id) ? id : '';
1003
+ }
999
1004
  /** Reflect a satellite's status into `satellites.<room>.*` states (created on first sight). */
1000
1005
  async updateSatelliteState(device, room, state) {
1001
1006
  const id = await this.ensureSatelliteObjects(device, room);
@@ -2480,18 +2485,38 @@ class Assistant extends adapter_core_1.Adapter {
2480
2485
  }
2481
2486
  return;
2482
2487
  }
2483
- // Scripts: start a countdown timer. message = { duration: seconds | "5 min", label?, room? }.
2488
+ // Scripts: start a countdown timer. message = { duration | seconds: seconds | "5 min", label | name?, room?,
2489
+ // satellite?, announce? }. With `satellite` (its id under satellites.*, or the device name) the timer is that
2490
+ // satellite's like one set there by voice: an ESPHome satellite shows and rings it, and the announcement plays
2491
+ // there. `announce` = what to say when it fires, word for word (default: "Timer finished: <name>.").
2484
2492
  if (obj.command === 'setTimer') {
2485
2493
  const m = (obj.message || {});
2486
- const duration = typeof m.duration === 'string'
2487
- ? (0, nlu_1.parseDurationSeconds)(m.duration, true) || 0
2488
- : Math.round(Number(m.duration) || 0);
2494
+ // `seconds` are seconds also as a string; a bare number in a `duration` string stays minutes as before
2495
+ const duration = m.seconds !== undefined
2496
+ ? Math.round(Number(m.seconds) || 0) ||
2497
+ (typeof m.seconds === 'string' ? (0, nlu_1.parseDurationSeconds)(m.seconds, false) || 0 : 0)
2498
+ : typeof m.duration === 'string'
2499
+ ? (0, nlu_1.parseDurationSeconds)(m.duration, true) || 0
2500
+ : Math.round(Number(m.duration) || 0);
2501
+ const satellite = this.timerSatellite(m.satellite);
2489
2502
  let result;
2490
- if (!this.timers || duration <= 0) {
2491
- result = { ok: false, error: this.timers ? 'duration must be > 0 seconds' : 'timers not ready' };
2503
+ if (!this.timers) {
2504
+ result = { ok: false, error: 'timers not ready' };
2505
+ }
2506
+ else if (duration <= 0) {
2507
+ result = { ok: false, error: 'duration must be > 0 seconds' };
2508
+ }
2509
+ else if (satellite === null) {
2510
+ result = { ok: false, error: `unknown satellite "${String(m.satellite)}"` };
2492
2511
  }
2493
2512
  else {
2494
- const info = this.timers.add({ label: m.label || '', room: m.room || '', source: '', duration });
2513
+ const info = this.timers.add({
2514
+ label: String(m.name ?? m.label ?? ''),
2515
+ room: m.room || '',
2516
+ source: satellite,
2517
+ duration,
2518
+ announce: typeof m.announce === 'string' ? m.announce : '',
2519
+ });
2495
2520
  result = { ok: true, id: info.id, fireAt: info.fireAt };
2496
2521
  }
2497
2522
  if (obj.callback) {
@@ -2499,19 +2524,37 @@ class Assistant extends adapter_core_1.Adapter {
2499
2524
  }
2500
2525
  return;
2501
2526
  }
2502
- // Scripts: cancel a timer by id, or all when no id is given. message = { id? }.
2527
+ // Scripts: cancel a timer by id, or all when no id is given. message = { id?, satellite? } — with `satellite`
2528
+ // only that satellite's timers.
2503
2529
  if (obj.command === 'cancelTimer') {
2504
2530
  const m = (obj.message || {});
2505
- const cancelled = m.id ? (this.timers?.cancel(m.id) ? 1 : 0) : (this.timers?.cancelAll() ?? 0);
2531
+ const satellite = this.timerSatellite(m.satellite);
2532
+ let result;
2533
+ if (satellite === null) {
2534
+ result = { ok: false, cancelled: 0, error: `unknown satellite "${String(m.satellite)}"` };
2535
+ }
2536
+ else {
2537
+ const mine = (this.timers?.list() || []).filter(t => !satellite || t.source === satellite);
2538
+ const ids = m.id ? mine.filter(t => t.id === m.id).map(t => t.id) : mine.map(t => t.id);
2539
+ const cancelled = satellite || m.id
2540
+ ? ids.filter(id => this.timers?.cancel(id)).length
2541
+ : (this.timers?.cancelAll() ?? 0);
2542
+ result = { ok: true, cancelled };
2543
+ }
2506
2544
  if (obj.callback) {
2507
- this.sendTo(obj.from, obj.command, { ok: true, cancelled }, obj.callback);
2545
+ this.sendTo(obj.from, obj.command, result, obj.callback);
2508
2546
  }
2509
2547
  return;
2510
2548
  }
2511
- // Scripts: list the running timers.
2549
+ // Scripts: list the running timers. message = { satellite? } — with it only that satellite's.
2512
2550
  if (obj.command === 'listTimers') {
2551
+ const m = (obj.message || {});
2552
+ const satellite = this.timerSatellite(m.satellite);
2553
+ const result = satellite === null
2554
+ ? { timers: [], error: `unknown satellite "${String(m.satellite)}"` }
2555
+ : { timers: (this.timers?.list() || []).filter(t => !satellite || t.source === satellite) };
2513
2556
  if (obj.callback) {
2514
- this.sendTo(obj.from, obj.command, { timers: this.timers?.list() || [] }, obj.callback);
2557
+ this.sendTo(obj.from, obj.command, result, obj.callback);
2515
2558
  }
2516
2559
  return;
2517
2560
  }
@@ -3150,9 +3193,10 @@ class Assistant extends adapter_core_1.Adapter {
3150
3193
  return Promise.resolve('');
3151
3194
  }
3152
3195
  const voice = VOICE_TRANSPORTS.has(origin.transport || '');
3196
+ const satellite = voice ? this.knownSatelliteId(origin.satellite || source, origin.room || '') : '';
3153
3197
  // The journal shows "[picture] What is in this picture?", like the conversation history
3154
3198
  const shown = (0, media_1.historyText)(question, attachments.images.length, !!attachments.video);
3155
- return this.requestOrigin.run({ source, room: origin.room || '', voice, attachments }, () => this.journal
3199
+ return this.requestOrigin.run({ source, room: origin.room || '', voice, satellite, attachments }, () => this.journal
3156
3200
  ? this.journal.run(shown, source, origin, () => {
3157
3201
  this.traceSpeechInput(question, origin);
3158
3202
  this.traceAttachments(attachments);
@@ -3677,7 +3721,11 @@ class Assistant extends adapter_core_1.Adapter {
3677
3721
  // pi-agent asks its questions to confirm a write ("… Say yes or no.") in this language, not in the system
3678
3722
  // language: a satellite listening in Russian does not hear a German "Ja"
3679
3723
  const language = String(this.config.voiceLanguage || this.language || '');
3680
- return await harness.ask({ text, conversation: source, instructions, language, images });
3724
+ // From a satellite: where pi-agent can speak later on its own (a reminder, the end of a background
3725
+ // task) — its `tts` state says it there.
3726
+ const satellite = this.requestOrigin.getStore()?.satellite || '';
3727
+ const tts = satellite ? `${this.namespace}.satellites.${satellite}.tts` : '';
3728
+ return await harness.ask({ text, conversation: source, instructions, language, images, satellite, tts });
3681
3729
  }
3682
3730
  catch (e) {
3683
3731
  if (localFallback) {
@@ -5033,13 +5081,25 @@ class Assistant extends adapter_core_1.Adapter {
5033
5081
  await this.setStateAsync(`${base}.duration`, { val: t.duration, ack: true });
5034
5082
  await this.setStateAsync(`${base}.fireAt`, { val: t.fireAt, ack: true });
5035
5083
  }
5084
+ /**
5085
+ * The `source` a timer of a satellite gets (the device name, like a timer set there by voice), from the satellite
5086
+ * a script named: '' when it named none, null when the name is unknown.
5087
+ */
5088
+ timerSatellite(name) {
5089
+ if (typeof name !== 'string' || !name.trim()) {
5090
+ return '';
5091
+ }
5092
+ const id = this.resolveSatelliteId(name);
5093
+ return id ? this.satDeviceById.get(id) || id : null;
5094
+ }
5036
5095
  /** A timer expired: record it, play the jingle (if any) and speak the announcement on the origin satellite. */
5037
5096
  onTimerFired(t) {
5038
5097
  this.log.info(`Timer fired: "${t.label || '(no label)'}"${t.room ? ` (${t.room})` : ''}`);
5039
5098
  this.setStateAsync('timers.lastFired', { val: t.label || t.id, ack: true }).catch(() => { });
5040
- const announce = this.config.timerAnnounce !== false;
5099
+ // A text a script gave for this timer (a reminder) is said even when timer announcements are off
5100
+ const announce = !!t.announce || this.config.timerAnnounce !== false;
5041
5101
  const target = this.announceTargetForSource(t.source);
5042
- this.fireEffects(this.config.timerSound || '', announce, this.timerAnnounceMessage(t), target);
5102
+ this.fireEffects(this.config.timerSound || '', announce, t.announce || this.timerAnnounceMessage(t), target);
5043
5103
  }
5044
5104
  /** Build the spoken expiry message ("Timer abgelaufen: die Nudeln.") in the interaction language. */
5045
5105
  timerAnnounceMessage(t) {