Skip to content

Commit

Permalink
anonymize and add Sonos functionality
Browse files Browse the repository at this point in the history
  • Loading branch information
theimo1221 committed Oct 8, 2021
1 parent d2c14b0 commit f2043c3
Show file tree
Hide file tree
Showing 7 changed files with 226 additions and 36 deletions.
17 changes: 6 additions & 11 deletions server/devices/groups/sonosGroup.ts
Original file line number Diff line number Diff line change
@@ -1,28 +1,23 @@
import { SonosDevice } from '@svrooij/sonos/lib';
import { SonosService } from '../../services/Sonos/sonos-service';
import { SNDevices } from '../../services/Sonos/SonosDevices';
import { OwnSonosDevice } from '../../services/Sonos/sonos-service';
import { Utils } from '../../services/utils/utils';
import { RoomBase } from '../../../models/rooms/RoomBase';

export class SonosGroup {
private _playing: boolean = false;
public constructor(private _room: RoomBase, public SNDevices: SNDevices[]) {}
public constructor(private _room: RoomBase, public ownSonosDevices: OwnSonosDevice[]) {}

public playRadio(radioUrl: string): void {
this.SNDevices.forEach((s) => {
const d: SonosDevice = SonosService.getDevice(s);
// d.TerminateQueue();
this.ownSonosDevices.forEach((s) => {
Utils.guardedTimeout(() => {
d.SetAVTransportURI(radioUrl);
s.device?.SetAVTransportURI(radioUrl);
}, 1500);
});
this._playing = true;
}

public turnOff(): void {
this.SNDevices.forEach((s) => {
const d: SonosDevice = SonosService.getDevice(s);
// d.TerminateQueue();
this.ownSonosDevices.forEach((s) => {
s.device?.Stop();
});
this._playing = false;
}
Expand Down
2 changes: 0 additions & 2 deletions server/devices/hmIPDevices/hmIpTuer.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,6 @@ import { TuerPosition } from './TuerPosition';
import { SonosService } from '/server/services/Sonos/sonos-service';
import { TelegramService } from '/server/services/Telegram/telegram-service';
import { Utils } from '/server/services/utils/utils';
import { SNDevices } from '/server/services/Sonos/SonosDevices';

export class HmIpTuer extends HmIPDevice {
public position: TuerPosition = TuerPosition.geschlossen;
Expand Down Expand Up @@ -78,7 +77,6 @@ export class HmIpTuer extends HmIPDevice {
ServerLogService.writeLog(LogLevel.Info, message);

TelegramService.inform(message);
SonosService.speakOnDevice(message, SNDevices.Buero);
this.minutesOpen = 0;
this._iOpen = undefined;
}
Expand Down
199 changes: 199 additions & 0 deletions server/services/Sonos/sonos-service.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,199 @@
import { SonosDevice, SonosManager } from '@svrooij/sonos/lib';
import { PlayNotificationOptions } from '@svrooij/sonos/lib/models';
import { ServerLogService } from '../log-service';
import { PollyService } from './polly-service';
import { LogLevel } from '/models/logLevel';
import { Utils } from '/server/services/utils/utils';
import { TelegramService } from '/server/services/Telegram/telegram-service';
import { TimeCallback, TimeCallbackType } from '/models/timeCallback';
import { TimeCallbackService } from '/server/services/time-callback-service';

export class OwnSonosDevice {
public maxPlayOnAllVolume: number = 80;
public playTestMessage() {
SonosService.speakOnDevice(`Ich bin ${this.name}`, this);
}
public constructor(
public name: string,
public roomName: string,
public device: SonosDevice | undefined,
) {};
}

export class SonosService {
private static sonosManager: SonosManager;
private static ownDevices: { [name: string]: OwnSonosDevice } = {};

private static isInitialized: boolean;
public static all: SonosDevice[] = [];
public static devicesDict: { [name: string]: SonosDevice } = {};
private static checkTimeCallback: TimeCallback;
private static reinitializationDevice: OwnSonosDevice | undefined;

public static addOwnDevices(
snDevices: { [name: string]: OwnSonosDevice },
reinitializationDevice?: OwnSonosDevice
): void {
this.ownDevices = snDevices;
this.reinitializationDevice = reinitializationDevice;
}

public static initialize(reinitialize: boolean = false): void {
ServerLogService.writeLog(LogLevel.Debug, `Initialisiere Sonos Service`);
if (!reinitialize) {
this.checkTimeCallback = new TimeCallback(
'SonosFunctionallityChecker',
TimeCallbackType.TimeOfDay,
() => {
this.checkAll();
},
0,
23,
30,
);
TimeCallbackService.addCallback(this.checkTimeCallback);
}
this.all = [];
this.sonosManager = new SonosManager();
this.sonosManager
.InitializeWithDiscovery(10)
.then(() => {
this.sonosManager.OnNewDevice((d: SonosDevice) => {
ServerLogService.writeLog(LogLevel.Info, `SonosDevice ${d.Name} joined`);
SonosService.initializeDevice(d);
});
ServerLogService.writeLog(LogLevel.Debug, `${this.sonosManager.Devices.length} Sonos Geräte gefunden.`);
this.sonosManager.Devices.forEach((d: SonosDevice) => {
SonosService.initializeDevice(d);
});
this.isInitialized = true;
if (!reinitialize && this.reinitializationDevice !== undefined) {
this.speakOnDevice(
`Sonos System initialisiert und bereit für Sprachausgaben.`,
this.reinitializationDevice,
30
);
}
})
.catch(console.error);
}

public static async checkAll(): Promise<void> {
let currentDevice: OwnSonosDevice | undefined;
try {
for (const deviceName in this.ownDevices) {
currentDevice = this.ownDevices[deviceName];
if(currentDevice?.device === undefined) {
throw `${currentDevice?.name} is missing`;
}
await currentDevice.device.GetState();
}
if(currentDevice !== undefined) {
ServerLogService.writeLog(LogLevel.Info, `Alle Geräte okay --> Last checked ${currentDevice.name}`);
}
} catch (e) {
ServerLogService.writeLog(
LogLevel.Error,
`Atleast one device failed --> Last checked ${(currentDevice?.name ?? "undefined")}`
);
TelegramService.inform(`Sonos device is failing --> Reinitialize whole system`);
this.initialize(true);
}
}

public static speakOnAll(pMessage: string, volumeOverride: number = -1): void {
if (!this.isInitialized) {
ServerLogService.writeLog(LogLevel.Alert, `SonosService noch nicht initialisiert.`);
}
PollyService.tts(pMessage, (networkPath: string, duration: number) => {
const hours: number = new Date().getHours();
let volume: number = hours < 10 || hours > 22 ? 40 : 80;

for (const deviceName in this.ownDevices) {
SonosService.playOnDevice(
this.ownDevices[deviceName],
networkPath,
duration,
(volumeOverride > -1) ? volumeOverride : Math.min(volume, this.ownDevices[deviceName].maxPlayOnAllVolume)
);
}
});
}

public static playOnDevice(
ownSnDevice: OwnSonosDevice,
mp3Name: string,
duration: number,
volume: number | undefined = undefined,
onlyWhenPlaying: boolean | undefined = undefined,
resolveAfterRevert: boolean | undefined = false,
): void {
const specificTimeout: number = Math.ceil(duration / 1000) + 5;
const options: PlayNotificationOptions = {
trackUri: `http://192.168.178.13:8081/file.mp3?fname=${mp3Name}`,
delayMs: 750,
onlyWhenPlaying: onlyWhenPlaying,
resolveAfterRevert: resolveAfterRevert,
volume: volume,
specificTimeout: specificTimeout,
notificationFired: (played) => {
ServerLogService.writeLog(
LogLevel.Trace,
`Sonos Notification ("${mp3Name}") was${played ? '' : "n't"} played in ${ownSnDevice.roomName} (duration: "${specificTimeout}")`,
);
},
};
try {
const device: SonosDevice| undefined = ownSnDevice.device;
if (device === undefined) {
ServerLogService.writeLog(LogLevel.Alert, `Sonos Geräte ${ownSnDevice.name} ist nicht initialisiert`);
Utils.guardedTimeout(
() => {
this.initialize();
},
500,
this,
);
return;
}
ServerLogService.writeLog(
LogLevel.Trace,
`Spiele nun die Ausgabe für "${mp3Name}" auf "${ownSnDevice.name}"`,
);
device.PlayNotificationTwo(options).then((played) => {
ServerLogService.writeLog(
LogLevel.Debug,
`Sonos Notification ("${mp3Name}") was${played ? '' : "n't"} played in ${
ownSnDevice.roomName
} (duration: "${specificTimeout}")`,
);
});
} catch (err) {
ServerLogService.writeLog(LogLevel.Info, `Sonos Error ${err.message}: ${err.stack}`);
}
}

public static speakOnDevice(
pMessage: string,
ownSnDevice: OwnSonosDevice,
volume: number | undefined = undefined,
onlyWhenPlaying: boolean | undefined = undefined,
resolveAfterRevert: boolean | undefined = undefined,
): void {
PollyService.tts(pMessage, (networkPath: string, duration: number) => {
SonosService.playOnDevice(ownSnDevice, networkPath, duration, volume, onlyWhenPlaying, resolveAfterRevert);
});
}

public static speakTestMessageOnEachDevice(): void {
for (const deviceName in this.ownDevices) {
this.ownDevices[deviceName].playTestMessage();
}
}

private static initializeDevice(d: SonosDevice) {
this.devicesDict[d.Name] = d;
this.ownDevices
ServerLogService.writeLog(LogLevel.Debug, `Sonos ${d.Uuid} für ${d.Name} gefunden`);
}
}
9 changes: 1 addition & 8 deletions server/services/Telegram/telegram-Commands.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,6 @@ import { Rolladen } from '../../devices/Rollos';
import { ZigbeeAquaraVibra } from '../../devices/zigbee/zigbeeAquaraVibra';
import { ZigbeeDeviceType } from '../../devices/zigbee/zigbeeDeviceType';
import { RoomBase } from '../../../models/rooms/RoomBase';
import { SNDevices } from '../Sonos/SonosDevices';
import { SonosService } from '../Sonos/sonos-service';

export class TelegramCommands {
Expand Down Expand Up @@ -204,13 +203,7 @@ export class TelegramCommands {
/\/perform_sonos_test/,
async (m: TelegramBot.Message): Promise<boolean> => {
if (m.from === undefined) return false;
SonosService.speakOnDevice(`Ich bin Küche!`, SNDevices.Kueche, 40);
SonosService.speakOnDevice(`Ich bin Schlafzimmer!`, SNDevices.Schlaf, 40);
SonosService.speakOnDevice(`Ich bin Wohnzimmer!`, SNDevices.Wohn, 40);
SonosService.speakOnDevice(`Ich bin Buero!`, SNDevices.Buero, 40);
SonosService.speakOnDevice(`Ich bin Fernsehzimmer!`, SNDevices.Fernseh, 40);
SonosService.speakOnDevice(`Ich bin Flur Erdgeschoss!`, SNDevices.EgFlur, 40);
SonosService.speakOnDevice(`Ich bin Hobbyraum!`, SNDevices.Hobbyraum, 40);
SonosService.speakTestMessageOnEachDevice();
TelegramService.sendMessage([m.from.id], 'Testnachricht gesprochen --> Führe weiteren Test durch');
SonosService.checkAll();
return true;
Expand Down
21 changes: 14 additions & 7 deletions server/services/calendar/müll-service.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
import { async, VEvent } from 'node-ical';
import { ServerLogService } from '../log-service';
import { SonosService } from '../Sonos/sonos-service';
import { SNDevices } from '../Sonos/SonosDevices';
import { OwnSonosDevice, SonosService } from '../Sonos/sonos-service';
import { TelegramService } from '../Telegram/telegram-service';
import { TimeCallbackService } from '../time-callback-service';
import { LogLevel } from '../../../models/logLevel';
Expand All @@ -14,7 +13,8 @@ export class MuellTonne {
public nextDate: Date | undefined = undefined;
public dates: Date[] = [];

public constructor(public name: string) {}
public constructor(public name: string, public ownSonosDevice?: OwnSonosDevice) {
}

public sortDates(): void {
this.dates = this.dates.sort((a, b) => a.getTime() - b.getTime());
Expand Down Expand Up @@ -61,19 +61,26 @@ export class MuellTonne {
if (nextTimestamp >= tomorowMidnight) {
const message = `Die Mülltonne mit dem Namen ${this.name} wird morgen abgeholt!`;
TelegramService.inform(message);
SonosService.speakOnDevice(message, SNDevices.Buero, 30);

if (this.ownSonosDevice) {
SonosService.speakOnDevice(message, this.ownSonosDevice, 30);
}
return;
}

if (nextTimestamp >= todayMidnight) {
if (new Date().getHours() > 10) {
const message = `Die Mülltonne mit dem Namen ${this.name} wurde heute abgeholt, Mülltonne zurückstellen!`;
TelegramService.inform(message);
SonosService.speakOnDevice(message, SNDevices.Buero, 30);
if (this.ownSonosDevice) {
SonosService.speakOnDevice(message, this.ownSonosDevice, 30);
}
} else {
const message = `Die Mülltonne mit dem Namen ${this.name} wird heute abgeholt!`;
TelegramService.inform(message);
SonosService.speakOnDevice(message, SNDevices.Buero, 30);
if (this.ownSonosDevice) {
SonosService.speakOnDevice(message, this.ownSonosDevice, 30);
}
}
return;
}
Expand Down Expand Up @@ -154,7 +161,7 @@ export class MuellService {
continue;
}

this.alleTonnen.push({ name: ev.summary, date: ev.start });
this.alleTonnen.push({name: ev.summary, date: ev.start});
switch (ev.summary) {
case this.gelbeTonne.name:
this.gelbeTonne.dates.push(ev.start);
Expand Down
9 changes: 4 additions & 5 deletions server/services/news-service.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,8 +3,7 @@ import { HTTPSOptions } from './HTTPSOptions';
import { ServerLogService } from './log-service';
import { LogLevel } from '../../models/logLevel';
import * as fs from 'fs';
import { SonosService } from './Sonos/sonos-service';
import { SNDevices } from './Sonos/SonosDevices';
import { OwnSonosDevice, SonosService } from './Sonos/sonos-service';
import { PollyService } from './Sonos/polly-service';
import { Utils } from './utils/utils';
import { SettingsService } from "/server/services/settings-service";
Expand Down Expand Up @@ -56,15 +55,15 @@ export class NewsService {
);
}

public static playLastNews(sonosDevice: SNDevices, volume: number = 30, retries: number = 5): void {
public static playLastNews(ownSonosDevice: OwnSonosDevice, volume: number = 30, retries: number = 5): void {
if (!NewsService.lastNewsName) {
if (retries > 0) {
ServerLogService.writeLog(
LogLevel.Warn,
`Der NewsService ist noch nicht bereit --> warten, verbleibende Neuversuche ${retries - 1}`,
);
Utils.guardedTimeout(() => {
NewsService.playLastNews(sonosDevice, volume, retries - 1);
NewsService.playLastNews(ownSonosDevice, volume, retries - 1);
}, 1000);
} else {
ServerLogService.writeLog(LogLevel.Error, `Der NewsService ist trotz Warten nicht bereit --> Abbruch`);
Expand All @@ -73,7 +72,7 @@ export class NewsService {
}

SonosService.playOnDevice(
sonosDevice,
ownSonosDevice,
NewsService.lastNewsName,
PollyService.getDuration(NewsService.lastNewsName),
volume,
Expand Down
5 changes: 2 additions & 3 deletions server/services/weather/weather-service.ts
Original file line number Diff line number Diff line change
@@ -1,8 +1,7 @@
import { HTTPSService } from '../https-service';
import { HTTPSOptions } from '../HTTPSOptions';
import { ServerLogService } from '../log-service';
import { SonosService } from '../Sonos/sonos-service';
import { SNDevices } from '../Sonos/SonosDevices';
import { OwnSonosDevice, SonosService } from '../Sonos/sonos-service';
import { WeatherAlert } from './weather-alert';
import { WeatherCurrent } from './weather-current';
import { WeatherDaily } from './weather-daily';
Expand Down Expand Up @@ -43,7 +42,7 @@ export class WeatherService {
}

public static playWeatherInfo(
sonosDevice: SNDevices,
sonosDevice: OwnSonosDevice,
volume: number = 30,
short: boolean = false,
retries = 5,
Expand Down

0 comments on commit f2043c3

Please sign in to comment.