Version 4.5.2 - even more configurable keypad beeps

This commit is contained in:
Chrystian Huot 2020-08-20 11:08:00 -04:00
parent 9067545799
commit 31984d0304
11 changed files with 499 additions and 282 deletions

View file

@ -1,6 +1,6 @@
{
"name": "rdio-scanner-client",
"version": "4.5.1",
"version": "4.5.2",
"lockfileVersion": 1,
"requires": true,
"dependencies": {

View file

@ -33,5 +33,5 @@
"build": "ng build --base-href ./ --prod",
"start": "ng serve"
},
"version": "4.5.1"
"version": "4.5.2"
}

View file

@ -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 {

View file

@ -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));
}
};

View file

@ -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;

View file

@ -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.

View file

@ -11,5 +11,5 @@
"start": "node run",
"update": "node update"
},
"version": "4.5.1"
"version": "4.5.2"
}

View file

@ -13,5 +13,11 @@
"ecmaVersion": 2018
},
"rules": {
"max-len": [
"error",
{
"code":140
}
]
}
}

View file

@ -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;

View file

@ -1,6 +1,6 @@
{
"name": "rdio-scanner-server",
"version": "4.5.1",
"version": "4.5.2",
"lockfileVersion": 1,
"requires": true,
"dependencies": {

View file

@ -39,5 +39,5 @@
"build": "webpack",
"start": "nodemon"
},
"version": "4.5.1"
"version": "4.5.2"
}