278 files

This commit is contained in:
Home Assistant Version Control committed 2026-09-13 20:38:58 +00:00
1 parent d7a0a08372
commit 1b4f5f68c6
278 files changed
+36892 -7150

No files matched your search

@@ -216,353 +216,26 @@ class TaskMateChildCard extends LitElement {
}
/**
* Get or create the AudioContext (lazy initialization)
* Must be called after user interaction due to browser autoplay policies
*/
_getAudioContext() {
if (!this._audioContext) {
this._audioContext = new (window.AudioContext || window.webkitAudioContext)();
}
// Resume if suspended (required after user interaction)
if (this._audioContext.state === 'suspended') {
this._audioContext.resume();
}
return this._audioContext;
}
/**
* Play a completion sound using Web Audio API
* Generates fun synthesized sounds - no external files needed!
* @param {string} soundName - Name of the sound to play (coin, levelup, fanfare, chime, powerup)
* Play a completion sound.
*
* Delegates to the shared engine in taskmate-sounds.js — this card used to
* carry its own copy of every synthesised sound, which drifted out of sync
* with the other copy in taskmate-config-sounds.js.
*
* @param {string} soundName - built-in name, or "custom:<file>" (#856)
*/
_playSound(soundName) {
// Don't play if sound is "none" or not specified
if (!soundName || soundName === 'none') {
return;
}
try {
const ctx = this._getAudioContext();
const now = ctx.currentTime;
switch (soundName) {
case 'coin':
this._playCoinSound(ctx, now);
break;
case 'levelup':
this._playLevelUpSound(ctx, now);
break;
case 'fanfare':
this._playFanfareSound(ctx, now);
break;
case 'chime':
this._playChimeSound(ctx, now);
break;
case 'powerup':
this._playPowerUpSound(ctx, now);
break;
case 'undo':
this._playUndoSound(ctx, now);
break;
case 'fart1':
this._playAudioFile('fart1.mp3');
break;
case 'fart2':
this._playAudioFile('fart2.mp3');
break;
case 'fart3':
this._playAudioFile('fart3.mp3');
break;
case 'fart4':
this._playAudioFile('fart4.mp3');
break;
case 'fart5':
this._playAudioFile('fart5.mp3');
break;
case 'fart6':
this._playAudioFile('fart6.mp3');
break;
case 'fart7':
this._playAudioFile('fart7.mp3');
break;
case 'fart8':
this._playAudioFile('fart8.mp3');
break;
case 'fart9':
this._playAudioFile('fart9.mp3');
break;
case 'fart10':
this._playAudioFile('fart10.mp3');
break;
case 'fart_random': {
// Pick a random fart sound (1-10)
const randomFartNum = Math.floor(Math.random() * 10) + 1;
this._playAudioFile(`fart${randomFartNum}.mp3`);
break;
}
default:
this._playCoinSound(ctx, now);
}
} catch (e) {
}
const engine = window.__taskmate_sounds;
if (!engine) return;
engine.play(soundName, this._customSounds());
}
/**
* Coin collect sound - classic video game coin pickup
* Two quick ascending tones
*/
_playCoinSound(ctx, startTime) {
const masterGain = ctx.createGain();
masterGain.connect(ctx.destination);
masterGain.gain.value = 0.3;
// First tone (E6)
const osc1 = ctx.createOscillator();
const gain1 = ctx.createGain();
osc1.connect(gain1);
gain1.connect(masterGain);
osc1.frequency.value = 1318.5; // E6
osc1.type = 'square';
gain1.gain.setValueAtTime(0.5, startTime);
gain1.gain.exponentialRampToValueAtTime(0.01, startTime + 0.1);
osc1.start(startTime);
osc1.stop(startTime + 0.1);
// Second tone (B6) - higher
const osc2 = ctx.createOscillator();
const gain2 = ctx.createGain();
osc2.connect(gain2);
gain2.connect(masterGain);
osc2.frequency.value = 1975.5; // B6
osc2.type = 'square';
gain2.gain.setValueAtTime(0.5, startTime + 0.08);
gain2.gain.exponentialRampToValueAtTime(0.01, startTime + 0.25);
osc2.start(startTime + 0.08);
osc2.stop(startTime + 0.25);
}
/**
* Level up sound - triumphant ascending arpeggio
*/
_playLevelUpSound(ctx, startTime) {
const masterGain = ctx.createGain();
masterGain.connect(ctx.destination);
masterGain.gain.value = 0.25;
// C major arpeggio going up: C5, E5, G5, C6
const notes = [523.25, 659.25, 783.99, 1046.5];
const duration = 0.12;
notes.forEach((freq, i) => {
const osc = ctx.createOscillator();
const gain = ctx.createGain();
osc.connect(gain);
gain.connect(masterGain);
osc.frequency.value = freq;
osc.type = 'square';
const noteStart = startTime + i * duration;
gain.gain.setValueAtTime(0.6, noteStart);
gain.gain.exponentialRampToValueAtTime(0.01, noteStart + duration + 0.1);
osc.start(noteStart);
osc.stop(noteStart + duration + 0.15);
});
// Final sustained chord
const chordNotes = [523.25, 659.25, 783.99]; // C major chord
const chordStart = startTime + notes.length * duration;
chordNotes.forEach((freq) => {
const osc = ctx.createOscillator();
const gain = ctx.createGain();
osc.connect(gain);
gain.connect(masterGain);
osc.frequency.value = freq;
osc.type = 'triangle';
gain.gain.setValueAtTime(0.3, chordStart);
gain.gain.exponentialRampToValueAtTime(0.01, chordStart + 0.5);
osc.start(chordStart);
osc.stop(chordStart + 0.55);
});
}
/**
* Fanfare sound - celebratory trumpet-like fanfare
*/
_playFanfareSound(ctx, startTime) {
const masterGain = ctx.createGain();
masterGain.connect(ctx.destination);
masterGain.gain.value = 0.2;
// Fanfare pattern: G4, G4, G4, E4, G4, C5 (classic celebration pattern)
const pattern = [
{ freq: 392.00, duration: 0.1, delay: 0 }, // G4
{ freq: 392.00, duration: 0.1, delay: 0.12 }, // G4
{ freq: 392.00, duration: 0.15, delay: 0.24 }, // G4
{ freq: 329.63, duration: 0.15, delay: 0.42 }, // E4
{ freq: 392.00, duration: 0.15, delay: 0.6 }, // G4
{ freq: 523.25, duration: 0.4, delay: 0.78 }, // C5 (long final note)
];
pattern.forEach(({ freq, duration, delay }) => {
const osc = ctx.createOscillator();
const gain = ctx.createGain();
osc.connect(gain);
gain.connect(masterGain);
osc.frequency.value = freq;
osc.type = 'sawtooth';
const noteStart = startTime + delay;
gain.gain.setValueAtTime(0.5, noteStart);
gain.gain.setValueAtTime(0.5, noteStart + duration * 0.8);
gain.gain.exponentialRampToValueAtTime(0.01, noteStart + duration);
osc.start(noteStart);
osc.stop(noteStart + duration + 0.05);
});
}
/**
* Chime sound - simple pleasant bell chime
*/
_playChimeSound(ctx, startTime) {
const masterGain = ctx.createGain();
masterGain.connect(ctx.destination);
masterGain.gain.value = 0.3;
// Bell-like sound using multiple harmonics
const fundamental = 880; // A5
const harmonics = [1, 2, 3, 4.2]; // Slight inharmonicity for bell-like quality
harmonics.forEach((harmonic, i) => {
const osc = ctx.createOscillator();
const gain = ctx.createGain();
osc.connect(gain);
gain.connect(masterGain);
osc.frequency.value = fundamental * harmonic;
osc.type = 'sine';
// Higher harmonics decay faster
const amplitude = 0.5 / (i + 1);
const decayTime = 0.8 / (i + 1);
gain.gain.setValueAtTime(amplitude, startTime);
gain.gain.exponentialRampToValueAtTime(0.001, startTime + decayTime);
osc.start(startTime);
osc.stop(startTime + decayTime + 0.1);
});
}
/**
* Power up sound - ascending sweep with sparkle
*/
_playPowerUpSound(ctx, startTime) {
const masterGain = ctx.createGain();
masterGain.connect(ctx.destination);
masterGain.gain.value = 0.25;
// Ascending sweep
const osc1 = ctx.createOscillator();
const gain1 = ctx.createGain();
osc1.connect(gain1);
gain1.connect(masterGain);
osc1.type = 'sawtooth';
osc1.frequency.setValueAtTime(200, startTime);
osc1.frequency.exponentialRampToValueAtTime(1200, startTime + 0.3);
gain1.gain.setValueAtTime(0.4, startTime);
gain1.gain.exponentialRampToValueAtTime(0.01, startTime + 0.35);
osc1.start(startTime);
osc1.stop(startTime + 0.4);
// Sparkle notes at the end
const sparkleNotes = [1318.5, 1567.98, 1975.5]; // E6, G6, B6
sparkleNotes.forEach((freq, i) => {
const osc = ctx.createOscillator();
const gain = ctx.createGain();
osc.connect(gain);
gain.connect(masterGain);
osc.frequency.value = freq;
osc.type = 'sine';
const noteStart = startTime + 0.25 + i * 0.05;
gain.gain.setValueAtTime(0.3, noteStart);
gain.gain.exponentialRampToValueAtTime(0.01, noteStart + 0.2);
osc.start(noteStart);
osc.stop(noteStart + 0.25);
});
}
/**
* Undo sound - sad descending "womp womp" style
* Two descending tones that sound disappointed/sad
*/
_playUndoSound(ctx, startTime) {
const masterGain = ctx.createGain();
masterGain.connect(ctx.destination);
masterGain.gain.value = 0.25;
// First "womp" - descending tone
const osc1 = ctx.createOscillator();
const gain1 = ctx.createGain();
osc1.connect(gain1);
gain1.connect(masterGain);
osc1.type = 'triangle';
osc1.frequency.setValueAtTime(311.13, startTime); // Eb4
osc1.frequency.exponentialRampToValueAtTime(233.08, startTime + 0.25); // Bb3
gain1.gain.setValueAtTime(0.6, startTime);
gain1.gain.exponentialRampToValueAtTime(0.3, startTime + 0.2);
gain1.gain.exponentialRampToValueAtTime(0.01, startTime + 0.3);
osc1.start(startTime);
osc1.stop(startTime + 0.35);
// Second "womp" - even lower descending tone (the sad part)
const osc2 = ctx.createOscillator();
const gain2 = ctx.createGain();
osc2.connect(gain2);
gain2.connect(masterGain);
osc2.type = 'triangle';
osc2.frequency.setValueAtTime(233.08, startTime + 0.3); // Bb3
osc2.frequency.exponentialRampToValueAtTime(155.56, startTime + 0.7); // Eb3
gain2.gain.setValueAtTime(0.5, startTime + 0.3);
gain2.gain.exponentialRampToValueAtTime(0.25, startTime + 0.55);
gain2.gain.exponentialRampToValueAtTime(0.01, startTime + 0.75);
osc2.start(startTime + 0.3);
osc2.stop(startTime + 0.8);
// Optional: add a subtle low vibrato for extra sadness
const osc3 = ctx.createOscillator();
const gain3 = ctx.createGain();
osc3.connect(gain3);
gain3.connect(masterGain);
osc3.type = 'sine';
osc3.frequency.setValueAtTime(116.54, startTime + 0.5); // Bb2 (sub bass)
gain3.gain.setValueAtTime(0.15, startTime + 0.5);
gain3.gain.exponentialRampToValueAtTime(0.01, startTime + 0.8);
osc3.start(startTime + 0.5);
osc3.stop(startTime + 0.85);
}
/**
* Play an audio file from the www folder
* Used for fart sounds (real audio files, not synthesized)
* CC0 public domain from BigSoundBank.com
* @param {string} filename - The audio file name (e.g., 'fart1.mp3')
*/
_playAudioFile(filename) {
try {
// Build the URL to the audio file in the www folder
const audio = new Audio(`/local/taskmate/${filename}`);
audio.volume = 1.0;
audio.play().catch(e => {
console.warn(`[TaskMate] Failed to play ${filename}:`, e);
});
} catch (e) {
console.warn(`[TaskMate] Error preparing audio ${filename}:`, e);
}
/** Uploaded custom sounds (#856). Lives on the chores sensor; the attr
* resolver merges it in, so the card reads it off the primary entity. */
_customSounds() {
const attrs = (window.__taskmate_attrs && window.__taskmate_attrs(this.hass, this.config?.entity))
|| this.hass?.states?.[this.config?.entity]?.attributes || {};
return attrs.custom_sounds || [];
}
static get styles() {