Implement severe weather alert tone
This commit is contained in:
parent
2e304d41c6
commit
d3643d3a9a
4 changed files with 98 additions and 1 deletions
BIN
server/alert/tone.mp3
Normal file
BIN
server/alert/tone.mp3
Normal file
Binary file not shown.
|
|
@ -1,6 +1,7 @@
|
|||
// hourly forecast list
|
||||
|
||||
import STATUS from './status.mjs';
|
||||
import { setAlertToneActive } from './media.mjs';
|
||||
import { safeJson } from './utils/fetch.mjs';
|
||||
import WeatherDisplay from './weatherdisplay.mjs';
|
||||
import { registerDisplay } from './navigation.mjs';
|
||||
|
|
@ -57,6 +58,7 @@ class Hazards extends WeatherDisplay {
|
|||
const superResult = super.getData(weatherParameters, refresh);
|
||||
if (!this.weatherParameters?.supportsNoaaAlerts) {
|
||||
this.data = [];
|
||||
setAlertToneActive(false);
|
||||
this.timing.totalScreens = 0;
|
||||
this.getDataCallback();
|
||||
this.setStatus(STATUS.loaded);
|
||||
|
|
@ -99,6 +101,7 @@ class Hazards extends WeatherDisplay {
|
|||
this.data = filteredAlerts;
|
||||
}
|
||||
this.alertSignature = getAlertSignature(this.data);
|
||||
setAlertToneActive(this.data.length > 0);
|
||||
const alertsChanged = previousSignature !== this.alertSignature;
|
||||
if (alertsChanged) {
|
||||
this.viewedAlerts.clear();
|
||||
|
|
@ -129,6 +132,7 @@ class Hazards extends WeatherDisplay {
|
|||
}
|
||||
} catch (error) {
|
||||
console.error(`Unexpected Active Alerts error: ${error.message}`);
|
||||
setAlertToneActive(false);
|
||||
if (this.isEnabled) this.setStatus(STATUS.failed);
|
||||
// return undefined to other subscribers
|
||||
this.getDataCallback(undefined);
|
||||
|
|
|
|||
|
|
@ -1,13 +1,22 @@
|
|||
import { text } from './utils/fetch.mjs';
|
||||
import Setting from './utils/setting.mjs';
|
||||
import { registerHiddenSetting } from './share.mjs';
|
||||
import { withBasePath } from './utils/base-path.mjs';
|
||||
|
||||
let playlist;
|
||||
let currentTrack = 0;
|
||||
let player;
|
||||
let alertTonePlayer;
|
||||
let sliderTimeout = null;
|
||||
let volumeSlider = null;
|
||||
let volumeSliderInput = null;
|
||||
let alertToneActive = false;
|
||||
let alertTonePending = false;
|
||||
let resumeMediaAfterAlertTone = false;
|
||||
let audioUnlocked = false;
|
||||
let alertToneTimeout = null;
|
||||
|
||||
const ALERT_TONE_DURATION_MS = 30_000;
|
||||
|
||||
const mediaPlaying = new Setting('mediaPlaying', {
|
||||
name: 'Media Playing',
|
||||
|
|
@ -33,11 +42,26 @@ document.addEventListener('DOMContentLoaded', () => {
|
|||
|
||||
// get the playlist
|
||||
getMedia();
|
||||
registerAudioUnlockHandlers();
|
||||
|
||||
// register the volume setting
|
||||
registerHiddenSetting(mediaVolume.elemId, mediaVolume);
|
||||
});
|
||||
|
||||
const unlockAudio = () => {
|
||||
if (audioUnlocked) return;
|
||||
audioUnlocked = true;
|
||||
if (alertToneActive && alertTonePending) {
|
||||
startAlertTone();
|
||||
}
|
||||
};
|
||||
|
||||
const registerAudioUnlockHandlers = () => {
|
||||
['pointerdown', 'keydown', 'touchstart'].forEach((eventName) => {
|
||||
document.addEventListener(eventName, unlockAudio, { passive: true, once: true });
|
||||
});
|
||||
};
|
||||
|
||||
const scanMusicDirectory = async () => {
|
||||
const parseDirectory = async (path, prefix = '') => {
|
||||
const listing = await text(path);
|
||||
|
|
@ -170,6 +194,7 @@ const startMedia = async () => {
|
|||
if (!player) {
|
||||
initializePlayer();
|
||||
} else {
|
||||
if (alertToneActive) return;
|
||||
try {
|
||||
await player.play();
|
||||
setTrackName(playlist.availableFiles[currentTrack]);
|
||||
|
|
@ -275,6 +300,73 @@ const initializePlayer = () => {
|
|||
volumeSliderInput.value = Math.round(mediaVolume.value * 100);
|
||||
};
|
||||
|
||||
const initializeAlertTonePlayer = () => {
|
||||
if (alertTonePlayer) return;
|
||||
alertTonePlayer = new Audio(withBasePath('alert/tone.mp3'));
|
||||
alertTonePlayer.type = 'audio/mpeg';
|
||||
alertTonePlayer.preload = 'auto';
|
||||
alertTonePlayer.addEventListener('ended', () => {
|
||||
if (alertToneActive) {
|
||||
alertTonePlayer.currentTime = 0;
|
||||
alertTonePlayer.play().catch((e) => {
|
||||
console.error('Couldn\'t continue alert tone');
|
||||
console.error(e);
|
||||
});
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
const startAlertTone = async () => {
|
||||
if (!audioUnlocked) {
|
||||
alertTonePending = true;
|
||||
return;
|
||||
}
|
||||
initializeAlertTonePlayer();
|
||||
try {
|
||||
await alertTonePlayer.play();
|
||||
alertTonePending = false;
|
||||
resumeMediaAfterAlertTone = mediaPlaying.value === true;
|
||||
if (alertToneTimeout) clearTimeout(alertToneTimeout);
|
||||
alertToneTimeout = setTimeout(() => {
|
||||
if (alertToneActive) {
|
||||
setAlertToneActive(false);
|
||||
}
|
||||
}, ALERT_TONE_DURATION_MS);
|
||||
if (player && !player.paused) {
|
||||
player.pause();
|
||||
}
|
||||
} catch (e) {
|
||||
console.error('Couldn\'t play alert tone');
|
||||
console.error(e);
|
||||
}
|
||||
};
|
||||
|
||||
const stopAlertTone = () => {
|
||||
alertTonePending = false;
|
||||
if (alertToneTimeout) {
|
||||
clearTimeout(alertToneTimeout);
|
||||
alertToneTimeout = null;
|
||||
}
|
||||
if (alertTonePlayer) {
|
||||
alertTonePlayer.pause();
|
||||
alertTonePlayer.currentTime = 0;
|
||||
}
|
||||
if (resumeMediaAfterAlertTone && mediaPlaying.value === true) {
|
||||
startMedia();
|
||||
}
|
||||
resumeMediaAfterAlertTone = false;
|
||||
};
|
||||
|
||||
const setAlertToneActive = (active) => {
|
||||
if (active === alertToneActive) return;
|
||||
alertToneActive = active;
|
||||
if (alertToneActive) {
|
||||
startAlertTone();
|
||||
return;
|
||||
}
|
||||
stopAlertTone();
|
||||
};
|
||||
|
||||
const playerCanPlay = async () => {
|
||||
// check to make sure they user still wants music (protect against slow loading music)
|
||||
if (!mediaPlaying.value) return;
|
||||
|
|
@ -304,6 +396,6 @@ const setTrackName = (fileName) => {
|
|||
};
|
||||
|
||||
export {
|
||||
// eslint-disable-next-line import/prefer-default-export
|
||||
handleClick,
|
||||
setAlertToneActive,
|
||||
};
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue