mirror of
https://github.com/chuot/rdio-scanner.git
synced 2026-08-13 08:33:00 -06:00
Version 4.5.2 - even more configurable keypad beeps
This commit is contained in:
parent
9067545799
commit
31984d0304
2
client/package-lock.json
generated
2
client/package-lock.json
generated
|
|
@ -1,6 +1,6 @@
|
|||
{
|
||||
"name": "rdio-scanner-client",
|
||||
"version": "4.5.1",
|
||||
"version": "4.5.2",
|
||||
"lockfileVersion": 1,
|
||||
"requires": true,
|
||||
"dependencies": {
|
||||
|
|
|
|||
|
|
@ -33,5 +33,5 @@
|
|||
"build": "ng build --base-href ./ --prod",
|
||||
"start": "ng serve"
|
||||
},
|
||||
"version": "4.5.1"
|
||||
"version": "4.5.2"
|
||||
}
|
||||
|
|
|
|||
|
|
@ -26,6 +26,7 @@ import { BehaviorSubject, NEVER, Subscription, timer } from 'rxjs';
|
|||
import { name as appName, version } from '../../../../package.json';
|
||||
import {
|
||||
RdioScannerAvoidOptions,
|
||||
RdioScannerBeepStyle,
|
||||
RdioScannerCall,
|
||||
RdioScannerConfig,
|
||||
RdioScannerEvent,
|
||||
|
|
@ -144,10 +145,15 @@ export class AppRdioScannerComponent implements AfterViewInit, OnDestroy {
|
|||
if (this.auth) {
|
||||
this.authFocus();
|
||||
|
||||
} else if (!this.liveFeedOffline) {
|
||||
this.appRdioScannerService.beep(options || this.call || this.callPrevious ? 1 : 3);
|
||||
} else {
|
||||
if (options || this.call || this.callPrevious) {
|
||||
this.appRdioScannerService.beep(RdioScannerBeepStyle.Activate);
|
||||
|
||||
this.appRdioScannerService.avoid(options);
|
||||
this.appRdioScannerService.avoid(options);
|
||||
|
||||
} else {
|
||||
this.appRdioScannerService.beep(RdioScannerBeepStyle.Denied);
|
||||
}
|
||||
|
||||
this.updateDimmer();
|
||||
}
|
||||
|
|
@ -176,10 +182,15 @@ export class AppRdioScannerComponent implements AfterViewInit, OnDestroy {
|
|||
if (this.auth) {
|
||||
this.authFocus();
|
||||
|
||||
} else if (!this.liveFeedOffline) {
|
||||
this.appRdioScannerService.beep(this.call || this.callPrevious ? this.holdSys ? 2 : 1 : 3);
|
||||
} else {
|
||||
if (this.call || this.callPrevious) {
|
||||
this.appRdioScannerService.beep(this.holdSys ? RdioScannerBeepStyle.Deactivate : RdioScannerBeepStyle.Activate);
|
||||
|
||||
this.appRdioScannerService.holdSystem();
|
||||
this.appRdioScannerService.holdSystem();
|
||||
|
||||
} else {
|
||||
this.appRdioScannerService.beep(RdioScannerBeepStyle.Denied);
|
||||
}
|
||||
|
||||
this.updateDimmer();
|
||||
}
|
||||
|
|
@ -189,10 +200,15 @@ export class AppRdioScannerComponent implements AfterViewInit, OnDestroy {
|
|||
if (this.auth) {
|
||||
this.authFocus();
|
||||
|
||||
} else if (!this.liveFeedOffline) {
|
||||
this.appRdioScannerService.beep(this.call || this.callPrevious ? this.holdTg ? 2 : 1 : 3);
|
||||
} else {
|
||||
if (this.call || this.callPrevious) {
|
||||
this.appRdioScannerService.beep(this.holdTg ? RdioScannerBeepStyle.Deactivate : RdioScannerBeepStyle.Activate);
|
||||
|
||||
this.appRdioScannerService.holdTalkgroup();
|
||||
this.appRdioScannerService.holdTalkgroup();
|
||||
|
||||
} else {
|
||||
this.appRdioScannerService.beep(RdioScannerBeepStyle.Denied);
|
||||
}
|
||||
|
||||
this.updateDimmer();
|
||||
}
|
||||
|
|
@ -204,17 +220,17 @@ export class AppRdioScannerComponent implements AfterViewInit, OnDestroy {
|
|||
|
||||
} else {
|
||||
if (this.liveFeedOffline) {
|
||||
this.appRdioScannerService.beep(2);
|
||||
this.appRdioScannerService.beep(RdioScannerBeepStyle.Deactivate);
|
||||
|
||||
this.stopOfflinePlay();
|
||||
|
||||
} else {
|
||||
this.appRdioScannerService.beep(this.liveFeedActive ? 2 : 1);
|
||||
this.appRdioScannerService.beep(this.liveFeedActive ? RdioScannerBeepStyle.Deactivate : RdioScannerBeepStyle.Activate);
|
||||
|
||||
this.appRdioScannerService.liveFeed();
|
||||
|
||||
this.updateDimmer();
|
||||
}
|
||||
|
||||
this.updateDimmer();
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -439,9 +455,14 @@ export class AppRdioScannerComponent implements AfterViewInit, OnDestroy {
|
|||
this.authFocus();
|
||||
|
||||
} else {
|
||||
this.appRdioScannerService.beep(this.liveFeedPaused ? 2 : 1);
|
||||
if (this.liveFeedPaused) {
|
||||
this.appRdioScannerService.beep(RdioScannerBeepStyle.Deactivate).then(() => this.appRdioScannerService.pause());
|
||||
|
||||
this.appRdioScannerService.pause();
|
||||
} else {
|
||||
this.appRdioScannerService.beep(RdioScannerBeepStyle.Activate);
|
||||
|
||||
this.appRdioScannerService.pause();
|
||||
}
|
||||
|
||||
this.updateDimmer();
|
||||
}
|
||||
|
|
@ -473,9 +494,14 @@ export class AppRdioScannerComponent implements AfterViewInit, OnDestroy {
|
|||
this.authFocus();
|
||||
|
||||
} else {
|
||||
this.appRdioScannerService.beep(this.call || this.callPrevious ? 1 : 3);
|
||||
if (!this.liveFeedPaused && (this.call || this.callPrevious)) {
|
||||
this.appRdioScannerService.beep(RdioScannerBeepStyle.Activate);
|
||||
|
||||
this.appRdioScannerService.replay();
|
||||
this.appRdioScannerService.replay();
|
||||
|
||||
} else {
|
||||
this.appRdioScannerService.beep(RdioScannerBeepStyle.Denied);
|
||||
}
|
||||
|
||||
this.updateDimmer();
|
||||
}
|
||||
|
|
@ -579,10 +605,15 @@ export class AppRdioScannerComponent implements AfterViewInit, OnDestroy {
|
|||
if (this.auth) {
|
||||
this.authFocus();
|
||||
|
||||
} else if (!this.callPending) {
|
||||
this.appRdioScannerService.beep(this.call || this.callPrevious ? 1 : 3);
|
||||
} else {
|
||||
if (this.call && !this.callPending) {
|
||||
this.appRdioScannerService.beep(RdioScannerBeepStyle.Activate);
|
||||
|
||||
this.appRdioScannerService.skip(options);
|
||||
this.appRdioScannerService.skip(options);
|
||||
|
||||
} else {
|
||||
this.appRdioScannerService.beep(RdioScannerBeepStyle.Denied);
|
||||
}
|
||||
|
||||
this.updateDimmer();
|
||||
}
|
||||
|
|
@ -804,19 +835,23 @@ export class AppRdioScannerComponent implements AfterViewInit, OnDestroy {
|
|||
}
|
||||
|
||||
private updateDimmer(): void {
|
||||
this.dimmerTimer?.unsubscribe();
|
||||
if (this.config?.useDimmer) {
|
||||
const delay = typeof this.config.useDimmer === 'boolean' ? 5000 : this.config.useDimmer * 1000;
|
||||
|
||||
this.dimmer = true;
|
||||
|
||||
this.dimmerTimer = timer(5 * 1000).subscribe(() => {
|
||||
this.dimmerTimer?.unsubscribe();
|
||||
|
||||
this.dimmerTimer = undefined;
|
||||
this.dimmer = true;
|
||||
|
||||
this.dimmer = false;
|
||||
this.dimmerTimer = timer(delay).subscribe(() => {
|
||||
this.dimmerTimer?.unsubscribe();
|
||||
|
||||
this.ngDetectChanges();
|
||||
});
|
||||
this.dimmerTimer = undefined;
|
||||
|
||||
this.dimmer = false;
|
||||
|
||||
this.ngDetectChanges();
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
private updateDisplay(time = this.callTime): void {
|
||||
|
|
|
|||
|
|
@ -23,6 +23,7 @@ import { interval, timer } from 'rxjs';
|
|||
import { takeWhile } from 'rxjs/operators';
|
||||
import {
|
||||
RdioScannerAvoidOptions,
|
||||
RdioScannerBeepStyle,
|
||||
RdioScannerCall,
|
||||
RdioScannerConfig,
|
||||
RdioScannerEvent,
|
||||
|
|
@ -70,7 +71,7 @@ export class AppRdioScannerService implements OnDestroy {
|
|||
|
||||
private config: RdioScannerConfig = {
|
||||
allowDownload: true,
|
||||
keyBeep: true,
|
||||
keypadBeeps: false,
|
||||
systems: [],
|
||||
useDimmer: true,
|
||||
useGroup: true,
|
||||
|
|
@ -165,25 +166,42 @@ export class AppRdioScannerService implements OnDestroy {
|
|||
});
|
||||
}
|
||||
|
||||
beep(style = 1): void {
|
||||
if (this.config.keyBeep) {
|
||||
switch (style) {
|
||||
case 1:
|
||||
this.beep1();
|
||||
break;
|
||||
beep(style = RdioScannerBeepStyle.Activate): Promise<void> {
|
||||
return new Promise((resolve) => {
|
||||
if (!this.beepContext || !this.config.keypadBeeps) {
|
||||
resolve();
|
||||
|
||||
case 2:
|
||||
this.beep2();
|
||||
break;
|
||||
|
||||
case 3:
|
||||
this.beep3();
|
||||
break;
|
||||
|
||||
default:
|
||||
this.beep1();
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
const context = this.beepContext;
|
||||
|
||||
const gn = context.createGain();
|
||||
|
||||
const seq = this.config.keypadBeeps[style];
|
||||
|
||||
gn.gain.value = .1;
|
||||
|
||||
gn.connect(context.destination);
|
||||
|
||||
seq.forEach((beep, index) => {
|
||||
const osc = context.createOscillator();
|
||||
|
||||
osc.connect(gn);
|
||||
|
||||
osc.frequency.value = beep.frequency;
|
||||
|
||||
osc.type = beep.type;
|
||||
|
||||
if (index === seq.length - 1) {
|
||||
osc.onended = () => resolve();
|
||||
}
|
||||
|
||||
osc.start(context.currentTime + beep.begin);
|
||||
|
||||
osc.stop(context.currentTime + beep.end);
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
holdSystem(options?: { resubscribe: boolean }): void {
|
||||
|
|
@ -489,98 +507,8 @@ export class AppRdioScannerService implements OnDestroy {
|
|||
}
|
||||
}
|
||||
|
||||
private beep1(): void {
|
||||
const context = this.beepContext;
|
||||
|
||||
if (context) {
|
||||
const gn = context.createGain();
|
||||
|
||||
const osc = context.createOscillator();
|
||||
|
||||
gn.connect(context.destination);
|
||||
|
||||
gn.gain.value = .1;
|
||||
|
||||
osc.connect(gn);
|
||||
|
||||
osc.frequency.value = 1200;
|
||||
|
||||
osc.type = 'square';
|
||||
|
||||
context.resume().then(() => {
|
||||
osc.start();
|
||||
|
||||
osc.stop(context.currentTime + .05);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
private beep2(): void {
|
||||
const context = this.beepContext;
|
||||
|
||||
if (context) {
|
||||
const gn = context.createGain();
|
||||
|
||||
const osc1 = context.createOscillator();
|
||||
const osc2 = context.createOscillator();
|
||||
|
||||
gn.connect(context.destination);
|
||||
|
||||
gn.gain.value = .1;
|
||||
|
||||
osc1.connect(gn);
|
||||
osc2.connect(gn);
|
||||
|
||||
osc1.frequency.value = 1200;
|
||||
osc2.frequency.value = 925;
|
||||
|
||||
osc1.type = 'square';
|
||||
osc2.type = 'square';
|
||||
|
||||
context.resume().then(() => {
|
||||
osc1.start();
|
||||
osc1.stop(context.currentTime + .1);
|
||||
|
||||
osc2.start(context.currentTime + .1);
|
||||
osc2.stop(context.currentTime + .2);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
private beep3(): void {
|
||||
const context = this.beepContext;
|
||||
|
||||
if (context) {
|
||||
const gn = context.createGain();
|
||||
|
||||
const osc1 = context.createOscillator();
|
||||
const osc2 = context.createOscillator();
|
||||
|
||||
gn.connect(context.destination);
|
||||
|
||||
gn.gain.value = .1;
|
||||
|
||||
osc1.connect(gn);
|
||||
osc2.connect(gn);
|
||||
|
||||
osc1.frequency.value = 925;
|
||||
osc2.frequency.value = 925;
|
||||
|
||||
osc1.type = 'square';
|
||||
osc2.type = 'square';
|
||||
|
||||
context.resume().then(() => {
|
||||
osc1.start();
|
||||
osc1.stop(context.currentTime + .05);
|
||||
|
||||
osc2.start(context.currentTime + .1);
|
||||
osc2.stop(context.currentTime + .15);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
private bootstrapAudio(): void {
|
||||
const events = ['mousedown', 'touchdown'];
|
||||
const events = ['keydown', 'mousedown', 'touchdown'];
|
||||
|
||||
const bootstrap = async () => {
|
||||
if (!this.audioContext) {
|
||||
|
|
@ -603,7 +531,13 @@ export class AppRdioScannerService implements OnDestroy {
|
|||
await this.audioContext.resume();
|
||||
|
||||
this.audioContext.onstatechange = () => resume();
|
||||
}
|
||||
|
||||
if (this.beepContext) {
|
||||
await this.beepContext.resume();
|
||||
}
|
||||
|
||||
if (this.audioContext && this.beepContext) {
|
||||
events.forEach((event) => document.body.removeEventListener(event, bootstrap));
|
||||
}
|
||||
};
|
||||
|
|
|
|||
|
|
@ -25,6 +25,19 @@ export interface RdioScannerAvoidOptions {
|
|||
status?: boolean;
|
||||
}
|
||||
|
||||
export interface RdioScannerBeep {
|
||||
begin: number;
|
||||
end: number;
|
||||
frequency: number;
|
||||
type: OscillatorType;
|
||||
}
|
||||
|
||||
export enum RdioScannerBeepStyle {
|
||||
Activate = 'activate',
|
||||
Deactivate = 'deactivate',
|
||||
Denied = 'denied',
|
||||
}
|
||||
|
||||
export interface RdioScannerCall {
|
||||
audio?: {
|
||||
type: 'Buffer';
|
||||
|
|
@ -59,9 +72,9 @@ export interface RdioScannerCallSource {
|
|||
|
||||
export interface RdioScannerConfig {
|
||||
allowDownload: boolean;
|
||||
keyBeep: boolean;
|
||||
keypadBeeps: RdioScannerKeypadBeeps | false;
|
||||
systems: RdioScannerSystem[];
|
||||
useDimmer: boolean;
|
||||
useDimmer: boolean | number;
|
||||
useGroup: boolean;
|
||||
useLed: boolean;
|
||||
}
|
||||
|
|
@ -92,6 +105,12 @@ export interface RdioScannerGroup {
|
|||
status: RdioScannerGroupStatus;
|
||||
}
|
||||
|
||||
export interface RdioScannerKeypadBeeps {
|
||||
[RdioScannerBeepStyle.Activate]: RdioScannerBeep[];
|
||||
[RdioScannerBeepStyle.Deactivate]: RdioScannerBeep[];
|
||||
[RdioScannerBeepStyle.Denied]: RdioScannerBeep[];
|
||||
}
|
||||
|
||||
export interface RdioScannerList {
|
||||
count: number;
|
||||
dateStart: Date;
|
||||
|
|
|
|||
137
docs/config.md
137
docs/config.md
|
|
@ -63,16 +63,18 @@ This file is at the heart of [Rdio Scanner](https://github.com/chuot/rdio-scanne
|
|||
"disableAudioConversion": false,
|
||||
|
||||
// (boolean) Emit beeps when clicking buttons
|
||||
// Default value is true
|
||||
"keyBeep": true,
|
||||
// Default value is true which is converted to Uniden style beeps
|
||||
// See KeypadBeeps section below for more details
|
||||
"keypadBeeps": true,
|
||||
|
||||
// (number) Clear the database for audio files older than the specified number of days
|
||||
// Default value is 7
|
||||
// Specifying a value of 0 will disable this feature
|
||||
"pruneDays": 7
|
||||
|
||||
// (boolean) Turn off the screen backlight when it is inactive
|
||||
// Default value is true
|
||||
// (boolean | number) Turn off the screen backlight when it is inactive
|
||||
// You can specify the delay as a number of seconds
|
||||
// Default value is true which the same as 5 seconds
|
||||
"useDimmer": true
|
||||
|
||||
// (boolean) Also toggle talkgroups based on their group assignment
|
||||
|
|
@ -494,6 +496,133 @@ Default value is an empty array `[]` .
|
|||
|
||||
> Note in this example for API key `d2079382-07df-4aa9-8940-8fb9e4ef5f2e` that systems 12, 15 and 31 will downstream all of their talkgroups, unlike systems 11 and 21 which will only downstream some of their talkgroups.
|
||||
|
||||
## KeypadBeeps - rdioScanner.options.keypadBeeps
|
||||
|
||||
Each button press can emit a specific beep depending on whether a function is activated, deactivated or denied.
|
||||
|
||||
**Structure of the rdioScanner.options.keypadBeeps object**
|
||||
|
||||
``` typescript
|
||||
options: {
|
||||
keypadBeeps: false | {
|
||||
activate: { // beeps sequence when a function is activated
|
||||
begin: number; // seconds
|
||||
end: number; // seconds
|
||||
frequency: number; // hertz
|
||||
type: 'sine' | 'square' | 'sawtooth' | 'triangle';
|
||||
}[],
|
||||
deactivate: { // beeps sequence when a function is deactivates
|
||||
begin: number; // seconds
|
||||
end: number; // seconds
|
||||
frequency: number; // hertz
|
||||
type: 'sine' | 'square' | 'sawtooth' | 'triangle';
|
||||
}[],
|
||||
denied: { // beeps sequence when a function is denied
|
||||
begin: number; // seconds
|
||||
end: number; // seconds
|
||||
frequency: number; // hertz
|
||||
type: 'sine' | 'square' | 'sawtooth' | 'triangle';
|
||||
}[],
|
||||
};
|
||||
}[]
|
||||
```
|
||||
|
||||
Default value is (*Uniden Style*):
|
||||
|
||||
``` json
|
||||
"options": {
|
||||
"keypadBeeps": {
|
||||
"activate": [
|
||||
{
|
||||
"begin": 0,
|
||||
"end": 0.05,
|
||||
"frequency": 1200,
|
||||
"type": "square"
|
||||
}
|
||||
],
|
||||
"deactivate": [
|
||||
{
|
||||
"begin": 0,
|
||||
"end": 0.1,
|
||||
"frequency": 1200,
|
||||
"type": "square"
|
||||
},
|
||||
{
|
||||
"begin": 0.1,
|
||||
"end": 0.2,
|
||||
"frequency": 925,
|
||||
"type": "square"
|
||||
}
|
||||
],
|
||||
"denied": [
|
||||
{
|
||||
"begin": 0,
|
||||
"end": 0.05,
|
||||
"frequency": 925,
|
||||
"type": "square"
|
||||
},
|
||||
{
|
||||
"begin": 0.1,
|
||||
"end": 0.15,
|
||||
"frequency": 925,
|
||||
"type": "square"
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
**Example - Whistler style**
|
||||
|
||||
``` json
|
||||
"options": {
|
||||
"keypadBeeps": {
|
||||
"activate": [
|
||||
{
|
||||
"begin": 0,
|
||||
"end": 0.05,
|
||||
"frequency": 2000,
|
||||
"type": "triangle"
|
||||
}
|
||||
],
|
||||
"deactivate": [
|
||||
{
|
||||
"begin": 0,
|
||||
"end": 0.04,
|
||||
"frequency": 1500,
|
||||
"type": "triangle"
|
||||
},
|
||||
{
|
||||
"begin": 0.04,
|
||||
"end": 0.08,
|
||||
"frequency": 1400,
|
||||
"type": "triangle"
|
||||
}
|
||||
],
|
||||
"denied": [
|
||||
{
|
||||
"begin": 0,
|
||||
"end": 0.04,
|
||||
"frequency": 1400,
|
||||
"type": "triangle"
|
||||
},
|
||||
{
|
||||
"begin": 0.05,
|
||||
"end": 0.09,
|
||||
"frequency": 1400,
|
||||
"type": "triangle"
|
||||
},
|
||||
{
|
||||
"begin": 0.1,
|
||||
"end": 0.14,
|
||||
"frequency": 1400,
|
||||
"type": "triangle"
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
## Systems - rdioScanner.systems
|
||||
|
||||
The heart of [Rdio Scanner](https://github.com/chuot/rdio-scanner) where all your systems and talkgroups are defined. Audio files to unknown systems / talkgroups will not be ingested.
|
||||
|
|
|
|||
|
|
@ -11,5 +11,5 @@
|
|||
"start": "node run",
|
||||
"update": "node update"
|
||||
},
|
||||
"version": "4.5.1"
|
||||
"version": "4.5.2"
|
||||
}
|
||||
|
|
|
|||
|
|
@ -13,5 +13,11 @@
|
|||
"ecmaVersion": 2018
|
||||
},
|
||||
"rules": {
|
||||
"max-len": [
|
||||
"error",
|
||||
{
|
||||
"code":140
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
|
|
@ -49,137 +49,7 @@ class Controller extends EventEmitter {
|
|||
|
||||
this.setMaxListeners(0);
|
||||
|
||||
this.config = ctx.config;
|
||||
|
||||
this.config.access = this.config.access !== undefined ? this.config.access : null;
|
||||
|
||||
this.config.apiKeys = this.config.apiKeys !== undefined ? this.config.apiKeys : uuid.v4();
|
||||
|
||||
if (this.config.options === null || typeof this.config.options !== 'object') {
|
||||
this.config.options = {};
|
||||
}
|
||||
|
||||
this.config.options.allowDownload = typeof this.config.options.allowDownload === 'boolean' ? this.config.options.allowDownload
|
||||
: typeof this.config.allowDownload === 'boolean' ? this.config.allowDownload : true;
|
||||
|
||||
if (this.config.allowDownload !== null || this.config.allowDownload !== undefined) {
|
||||
delete this.config.allowDownload;
|
||||
}
|
||||
|
||||
this.config.options.disableAudioConversion = typeof this.config.options.disableAudioConversion === 'boolean' ? this.config.options.disableAudioConversion
|
||||
: typeof this.config.disableAudioConversion === 'boolean' ? this.config.disableAudioConversion : false;
|
||||
|
||||
if (this.config.disableAudioConversion !== null || this.config.disableAudioConversion !== undefined) {
|
||||
delete this.config.disableAudioConversion;
|
||||
}
|
||||
|
||||
this.config.options.keyBeep = typeof this.config.options.keyBeep === 'boolean' ? this.config.options.keyBeep : true;
|
||||
|
||||
this.config.options.pruneDays = typeof this.config.options.pruneDays === 'number' ? this.config.options.pruneDays
|
||||
: typeof this.config.pruneDays === 'number' ? this.config.pruneDays : 7;
|
||||
|
||||
if (this.config.pruneDays !== null || this.config.pruneDays !== undefined) {
|
||||
delete this.config.pruneDays;
|
||||
}
|
||||
|
||||
this.config.options.useDimmer = typeof this.config.options.useDimmer === 'boolean' ? this.config.options.useDimmer
|
||||
: typeof this.config.useDimmer === 'boolean' ? this.config.useDimmer : true;
|
||||
|
||||
if (this.config.useDimmer !== null || this.config.useDimmer !== undefined) {
|
||||
delete this.config.useDimmer;
|
||||
}
|
||||
|
||||
this.config.options.useGroup = typeof this.config.options.useGroup === 'boolean' ? this.config.options.useGroup
|
||||
: typeof this.config.useGroup === 'boolean' ? this.config.useGroup : true;
|
||||
|
||||
if (this.config.useGroup !== null || this.config.useGroup !== undefined) {
|
||||
delete this.config.useGroup;
|
||||
}
|
||||
|
||||
this.config.options.useLed = typeof this.config.options.useLed === 'boolean' ? this.config.options.useLed
|
||||
: typeof this.config.useLed === 'boolean' ? this.config.useLed : true;
|
||||
|
||||
if (this.config.useLed !== null || this.config.useLed !== undefined) {
|
||||
delete this.config.useLed;
|
||||
}
|
||||
|
||||
this.config.systems = Array.isArray(this.config.systems) ? this.config.systems : systemsDefault;
|
||||
|
||||
this.config.systems.forEach((system) => {
|
||||
if (system === null || typeof system !== 'object') {
|
||||
console.error(`Config: Unknown system definition: ${JSON.stringify(system)}`);
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
if (typeof system.id !== 'number') {
|
||||
console.error(`Config: System.id ${JSON.stringify(system.id)} not a number`);
|
||||
}
|
||||
|
||||
if (typeof system.label !== 'string' || !system.label.length) {
|
||||
console.error(`Config: System ${system.id}, label not a string: ${JSON.stringify(system.label)}`);
|
||||
}
|
||||
|
||||
if (!Array.isArray(system.talkgroups)) {
|
||||
system.talkgroups = [];
|
||||
}
|
||||
|
||||
if (!system.talkgroups.length) {
|
||||
console.error(`Config: System ${system.id}, no talkgroups`);
|
||||
}
|
||||
|
||||
system.talkgroups.forEach((talkgroup) => {
|
||||
if (talkgroup === null || typeof talkgroup !== 'object') {
|
||||
console.error(`Config: System ${system.id}, unknown talkgroup definition: ${JSON.stringify(talkgroup)}`);
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
if (typeof talkgroup.id !== 'number') {
|
||||
console.error(`Config: System ${system.id}, talkgroup.id not a number: ${JSON.stringify(talkgroup.id)}`);
|
||||
}
|
||||
|
||||
if (this.config.useGroup && typeof talkgroup.group !== 'string' || !talkgroup.group.length) {
|
||||
console.error(`Config: System ${system.id}, talkgroup ${talkgroup.id}, group not a string: ${JSON.stringify(talkgroup.group)}`);
|
||||
}
|
||||
|
||||
if (typeof talkgroup.label !== 'string' || !talkgroup.label.length) {
|
||||
console.error(`Config: System ${system.id}, talkgroup ${talkgroup.id}, label not a string: ${JSON.stringify(talkgroup.label)}`);
|
||||
}
|
||||
|
||||
if (typeof talkgroup.name !== 'string' || !talkgroup.name.length) {
|
||||
console.error(`Config: System ${system.id}, talkgroup ${talkgroup.id}, name not a string: ${JSON.stringify(talkgroup.name)}`);
|
||||
}
|
||||
|
||||
if (talkgroup.patches !== undefined && !Array.isArray(talkgroup.patches)) {
|
||||
console.error(`Config: System ${system.id}, talkgroup ${talkgroup.id}, patches not an array: ${JSON.stringify(talkgroup.patches)}`);
|
||||
}
|
||||
|
||||
if (typeof talkgroup.tag !== 'string' || !talkgroup.tag.length) {
|
||||
console.error(`Config: System ${system.id}, talkgroup ${talkgroup.id}, tag not a string: ${JSON.stringify(talkgroup.tag)}`);
|
||||
}
|
||||
});
|
||||
|
||||
if (!Array.isArray(system.units)) {
|
||||
system.units = [];
|
||||
}
|
||||
|
||||
system.units.forEach((unit) => {
|
||||
if (unit === null || typeof unit !== 'object') {
|
||||
console.error(`Config: System ${system.id}, unknown unit definition: ${JSON.stringify(unit)}`);
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
if (typeof unit.id !== 'number') {
|
||||
console.error(`Config: System ${system.id}, unit.id not a number: ${JSON.stringify(unit.id)}`);
|
||||
}
|
||||
|
||||
if (typeof unit.label !== 'string' || !unit.label.length) {
|
||||
console.error(`Config: System ${system.id}, unit ${unit.id}, label not a string: ${JSON.stringify(unit.label)}`);
|
||||
}
|
||||
});
|
||||
})
|
||||
this.config = parseConfig(ctx.config);
|
||||
|
||||
this.models = ctx.models;
|
||||
|
||||
|
|
@ -384,7 +254,7 @@ class Controller extends EventEmitter {
|
|||
|
||||
getOptions() {
|
||||
return {
|
||||
keyBeep: this.config.options.keyBeep,
|
||||
keypadBeeps: this.config.options.keypadBeeps,
|
||||
useDimmer: this.config.options.useDimmer,
|
||||
useGroup: this.config.options.useGroup,
|
||||
useLed: this.config.options.useLed,
|
||||
|
|
@ -481,7 +351,8 @@ class Controller extends EventEmitter {
|
|||
},
|
||||
});
|
||||
|
||||
return systems.map((system) => (system.talkgroups = system.talkgroups.filter((tg) => scope[system.id].includes(tg.id))) && system);
|
||||
return systems.map((system) => (system.talkgroups = system.talkgroups
|
||||
.filter((tg) => scope[system.id].includes(tg.id))) && system);
|
||||
|
||||
} else {
|
||||
const systems = await this.models.system.findAll();
|
||||
|
|
@ -508,7 +379,8 @@ class Controller extends EventEmitter {
|
|||
}) : null;
|
||||
|
||||
if (!system || !talkgroup) {
|
||||
console.log(`NewCall: system=${call.system || 'unknown'} talkgroup=${call.talkgroup || 'unknown'} file=${call.audioName} No matching system/talkgroup`);
|
||||
console.log(`NewCall: system=${call.system || 'unknown'} talkgroup=${call.talkgroup || 'unknown'} `
|
||||
+ `file=${call.audioName} No matching system/talkgroup`);
|
||||
|
||||
return;
|
||||
}
|
||||
|
|
@ -702,4 +574,226 @@ class Controller extends EventEmitter {
|
|||
}
|
||||
}
|
||||
|
||||
function parseConfig(config) {
|
||||
const ledColors = ['blue', 'cyan', 'green', 'magenta', 'red', 'white', 'yellow']
|
||||
|
||||
const oscillatorNodeType = ['sine', 'square', 'sawtooth', 'triangle'];
|
||||
|
||||
config.access = config.access !== undefined ? config.access : null;
|
||||
|
||||
config.apiKeys = config.apiKeys !== undefined ? config.apiKeys : uuid.v4();
|
||||
|
||||
if (config.options === null || typeof config.options !== 'object') {
|
||||
config.options = {};
|
||||
}
|
||||
|
||||
config.options.allowDownload = typeof config.options.allowDownload === 'boolean' ? config.options.allowDownload
|
||||
: typeof config.allowDownload === 'boolean' ? config.allowDownload : true;
|
||||
|
||||
if (config.allowDownload !== null || config.allowDownload !== undefined) {
|
||||
delete config.allowDownload;
|
||||
}
|
||||
|
||||
config.options.disableAudioConversion = typeof config.options.disableAudioConversion === 'boolean'
|
||||
? config.options.disableAudioConversion : typeof config.disableAudioConversion === 'boolean'
|
||||
? config.disableAudioConversion : false;
|
||||
|
||||
if (config.disableAudioConversion !== null || config.disableAudioConversion !== undefined) {
|
||||
delete config.disableAudioConversion;
|
||||
}
|
||||
|
||||
config.options.keypadBeeps = config.options.keypadBeeps === false ? false : config.options.keyBeep === false
|
||||
? false : config.options.keypadBeeps !== null && typeof config.options.keypadBeeps === 'object'
|
||||
? config.options.keypadBeeps : {};
|
||||
|
||||
if (config.options.keyBeep !== undefined) {
|
||||
delete config.options.keyBeep;
|
||||
}
|
||||
|
||||
if (typeof config.options.keypadBeeps === 'object') {
|
||||
if (Array.isArray(config.options.keypadBeeps.activate)) {
|
||||
config.options.keypadBeeps.activate.forEach((beep, index) => {
|
||||
beep.begin = typeof beep.begin === 'number' ? beep.begin : index * 0.05;
|
||||
beep.end = typeof beep.end === 'number' ? beep.end : index * 0.05 + 0.05;
|
||||
beep.frequency = typeof beep.frequency === 'number' ? beep.frequency : 1200;
|
||||
beep.type = oscillatorNodeType.includes(beep.type) ? beep.type : 'square';
|
||||
});
|
||||
|
||||
} else {
|
||||
config.options.keypadBeeps.activate = [{
|
||||
begin: 0,
|
||||
end: 0.05,
|
||||
frequency: 1200,
|
||||
type: 'square',
|
||||
}];
|
||||
|
||||
}
|
||||
|
||||
if (Array.isArray(config.options.keypadBeeps.deactivate)) {
|
||||
config.options.keypadBeeps.deactivate.forEach((beep, index) => {
|
||||
beep.begin = typeof beep.begin === 'number' ? beep.begin : index * 0.1;
|
||||
beep.end = typeof beep.end === 'number' ? beep.end : index * 0.1 + 0.1;
|
||||
beep.frequency = typeof beep.frequency === 'number' ? beep.frequency : 925;
|
||||
beep.type = oscillatorNodeType.includes(beep.type) ? beep.type : 'square';
|
||||
});
|
||||
|
||||
} else {
|
||||
config.options.keypadBeeps.deactivate = [{
|
||||
begin: 0,
|
||||
end: 0.1,
|
||||
frequency: 1200,
|
||||
type: 'square',
|
||||
}, {
|
||||
begin: 0.1,
|
||||
end: 0.2,
|
||||
frequency: 925,
|
||||
type: 'square',
|
||||
}];
|
||||
}
|
||||
|
||||
if (Array.isArray(config.options.keypadBeeps.denied)) {
|
||||
config.options.keypadBeeps.denied.forEach((beep, index) => {
|
||||
beep.begin = typeof beep.begin === 'number' ? beep.begin : index * 0.1;
|
||||
beep.end = typeof beep.end === 'number' ? beep.end : index * 0.1 + 0.05;
|
||||
beep.frequency = typeof beep.frequency === 'number' ? beep.frequency : 925;
|
||||
beep.type = oscillatorNodeType.includes(beep.type) ? beep.type : 'square';
|
||||
});
|
||||
|
||||
} else {
|
||||
config.options.keypadBeeps.denied = [{
|
||||
begin: 0,
|
||||
end: 0.05,
|
||||
frequency: 925,
|
||||
type: 'square',
|
||||
}, {
|
||||
begin: 0.1,
|
||||
end: 0.15,
|
||||
frequency: 925,
|
||||
type: 'square',
|
||||
}];
|
||||
}
|
||||
}
|
||||
|
||||
config.options.pruneDays = typeof config.options.pruneDays === 'number' ? config.options.pruneDays
|
||||
: typeof config.pruneDays === 'number' ? config.pruneDays : 7;
|
||||
|
||||
if (config.pruneDays !== null || config.pruneDays !== undefined) {
|
||||
delete config.pruneDays;
|
||||
}
|
||||
|
||||
config.options.useDimmer = typeof config.options.useDimmer === 'number' ? config.options.useDimmer
|
||||
: typeof config.options.useDimmer === 'boolean' ? config.options.useDimmer
|
||||
: typeof config.useDimmer === 'boolean' ? config.useDimmer : true;
|
||||
|
||||
if (config.useDimmer !== null || config.useDimmer !== undefined) {
|
||||
delete config.useDimmer;
|
||||
}
|
||||
|
||||
config.options.useGroup = typeof config.options.useGroup === 'boolean' ? config.options.useGroup
|
||||
: typeof config.useGroup === 'boolean' ? config.useGroup : true;
|
||||
|
||||
if (config.useGroup !== null || config.useGroup !== undefined) {
|
||||
delete config.useGroup;
|
||||
}
|
||||
|
||||
config.options.useLed = typeof config.options.useLed === 'boolean' ? config.options.useLed
|
||||
: typeof config.useLed === 'boolean' ? config.useLed : true;
|
||||
|
||||
if (config.useLed !== null || config.useLed !== undefined) {
|
||||
delete config.useLed;
|
||||
}
|
||||
|
||||
config.systems = Array.isArray(config.systems) ? config.systems : systemsDefault;
|
||||
|
||||
config.systems.forEach((system) => {
|
||||
if (system === null || typeof system !== 'object') {
|
||||
console.error(`Config: Unknown system definition: ${JSON.stringify(system)}`);
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
if (typeof system.id !== 'number') {
|
||||
console.error(`Config: System.id ${JSON.stringify(system.id)} not a number`);
|
||||
}
|
||||
|
||||
if (typeof system.label !== 'string' || !system.label.length) {
|
||||
console.error(`Config: System ${system.id}, label not a string: ${JSON.stringify(system.label)}`);
|
||||
}
|
||||
|
||||
if (typeof system.led !== 'undefined') {
|
||||
system.led = ledColors.includes(system.led) ? system.led : 'green';
|
||||
}
|
||||
|
||||
if (!Array.isArray(system.talkgroups)) {
|
||||
system.talkgroups = [];
|
||||
}
|
||||
|
||||
if (!system.talkgroups.length) {
|
||||
console.error(`Config: System ${system.id}, no talkgroups`);
|
||||
}
|
||||
|
||||
system.talkgroups.forEach((talkgroup) => {
|
||||
if (talkgroup === null || typeof talkgroup !== 'object') {
|
||||
console.error(`Config: System ${system.id}, unknown talkgroup definition: ${JSON.stringify(talkgroup)}`);
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
if (typeof talkgroup.id !== 'number') {
|
||||
console.error(`Config: System ${system.id}, talkgroup.id not a number: ${JSON.stringify(talkgroup.id)}`);
|
||||
}
|
||||
|
||||
if (config.useGroup && typeof talkgroup.group !== 'string' || !talkgroup.group.length) {
|
||||
console.error(`Config: System ${system.id}, talkgroup ${talkgroup.id}, `
|
||||
+ `group not a string: ${JSON.stringify(talkgroup.group)}`);
|
||||
}
|
||||
|
||||
if (typeof talkgroup.label !== 'string' || !talkgroup.label.length) {
|
||||
console.error(`Config: System ${system.id}, talkgroup ${talkgroup.id}, `
|
||||
+ `label not a string: ${JSON.stringify(talkgroup.label)}`);
|
||||
}
|
||||
|
||||
if (typeof talkgroup.led !== 'undefined') {
|
||||
talkgroup.led = ledColors.includes(talkgroup.led) ? talkgroup.led : 'green';
|
||||
}
|
||||
|
||||
if (typeof talkgroup.name !== 'string' || !talkgroup.name.length) {
|
||||
console.error(`Config: System ${system.id}, talkgroup ${talkgroup.id}, `
|
||||
+ `name not a string: ${JSON.stringify(talkgroup.name)}`);
|
||||
}
|
||||
|
||||
if (talkgroup.patches !== undefined && !Array.isArray(talkgroup.patches)) {
|
||||
console.error(`Config: System ${system.id}, talkgroup ${talkgroup.id}, `
|
||||
+ `patches not an array: ${JSON.stringify(talkgroup.patches)}`);
|
||||
}
|
||||
|
||||
if (typeof talkgroup.tag !== 'string' || !talkgroup.tag.length) {
|
||||
console.error(`Config: System ${system.id}, talkgroup ${talkgroup.id}, tag not a string: ${JSON.stringify(talkgroup.tag)}`);
|
||||
}
|
||||
});
|
||||
|
||||
if (!Array.isArray(system.units)) {
|
||||
system.units = [];
|
||||
}
|
||||
|
||||
system.units.forEach((unit) => {
|
||||
if (unit === null || typeof unit !== 'object') {
|
||||
console.error(`Config: System ${system.id}, unknown unit definition: ${JSON.stringify(unit)}`);
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
if (typeof unit.id !== 'number') {
|
||||
console.error(`Config: System ${system.id}, unit.id not a number: ${JSON.stringify(unit.id)}`);
|
||||
}
|
||||
|
||||
if (typeof unit.label !== 'string' || !unit.label.length) {
|
||||
console.error(`Config: System ${system.id}, unit ${unit.id}, label not a string: ${JSON.stringify(unit.label)}`);
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
return config;
|
||||
}
|
||||
|
||||
module.exports = Controller;
|
||||
|
|
|
|||
2
server/package-lock.json
generated
2
server/package-lock.json
generated
|
|
@ -1,6 +1,6 @@
|
|||
{
|
||||
"name": "rdio-scanner-server",
|
||||
"version": "4.5.1",
|
||||
"version": "4.5.2",
|
||||
"lockfileVersion": 1,
|
||||
"requires": true,
|
||||
"dependencies": {
|
||||
|
|
|
|||
|
|
@ -39,5 +39,5 @@
|
|||
"build": "webpack",
|
||||
"start": "nodemon"
|
||||
},
|
||||
"version": "4.5.1"
|
||||
"version": "4.5.2"
|
||||
}
|
||||
|
|
|
|||
Loading…
Reference in a new issue