Snapcast service
This commit is contained in:
131
src/js/services/snapcast/actions.js
Executable file
131
src/js/services/snapcast/actions.js
Executable file
@ -0,0 +1,131 @@
|
||||
|
||||
export function connect(){
|
||||
return {
|
||||
type: 'SNAPCAST_CONNECT'
|
||||
}
|
||||
}
|
||||
|
||||
export function disconnect(){
|
||||
return {
|
||||
type: 'SNAPCAST_DISCONNECT'
|
||||
}
|
||||
}
|
||||
|
||||
export function reload(){
|
||||
return {
|
||||
type: 'SNAPCAST_RELOAD'
|
||||
}
|
||||
}
|
||||
|
||||
export function getConnections(){
|
||||
return {
|
||||
type: 'SNAPCAST_GET_CONNECTIONS'
|
||||
}
|
||||
}
|
||||
|
||||
export function connectionAdded(connection){
|
||||
return {
|
||||
type: 'SNAPCAST_CONNECTION_ADDED',
|
||||
connection: connection
|
||||
}
|
||||
}
|
||||
|
||||
export function connectionChanged(connection){
|
||||
return {
|
||||
type: 'SNAPCAST_CONNECTION_CHANGED',
|
||||
connection: connection
|
||||
}
|
||||
}
|
||||
|
||||
export function connectionRemoved(connection){
|
||||
return {
|
||||
type: 'SNAPCAST_CONNECTION_REMOVED',
|
||||
connection: connection
|
||||
}
|
||||
}
|
||||
|
||||
export function getServer(){
|
||||
return {
|
||||
type: 'SNAPCAST_GET_SERVER'
|
||||
}
|
||||
}
|
||||
|
||||
export function serverLoaded(server){
|
||||
return {
|
||||
type: 'SNAPCAST_SERVER_LOADED',
|
||||
server: server
|
||||
}
|
||||
}
|
||||
|
||||
export function setClientName(id, name){
|
||||
return {
|
||||
type: 'SNAPCAST_SET_CLIENT_NAME',
|
||||
id: id,
|
||||
name: name
|
||||
}
|
||||
}
|
||||
|
||||
export function setClientMute(id, mute){
|
||||
return {
|
||||
type: 'SNAPCAST_SET_CLIENT_MUTE',
|
||||
id: id,
|
||||
mute: mute
|
||||
}
|
||||
}
|
||||
|
||||
export function setClientVolume(id, percent){
|
||||
return {
|
||||
type: 'SNAPCAST_SET_CLIENT_VOLUME',
|
||||
id: id,
|
||||
percent: percent
|
||||
}
|
||||
}
|
||||
|
||||
export function setClientLatency(id, latency){
|
||||
return {
|
||||
type: 'SNAPCAST_SET_CLIENT_LATENCY',
|
||||
id: id,
|
||||
latency: latency
|
||||
}
|
||||
}
|
||||
|
||||
export function setClientGroup(id, group_id){
|
||||
return {
|
||||
type: 'SNAPCAST_SET_CLIENT_GROUP',
|
||||
id: id,
|
||||
group_id: group_id
|
||||
}
|
||||
}
|
||||
|
||||
export function deleteClient(id){
|
||||
return {
|
||||
type: 'SNAPCAST_DELETE_CLIENT',
|
||||
id: id
|
||||
}
|
||||
}
|
||||
|
||||
export function setGroupStream(id, stream_id){
|
||||
return {
|
||||
type: 'SNAPCAST_SET_GROUP_STREAM',
|
||||
id: id,
|
||||
stream_id: stream_id
|
||||
}
|
||||
}
|
||||
|
||||
export function setGroupMute(id, mute){
|
||||
return {
|
||||
type: 'SNAPCAST_SET_GROUP_MUTE',
|
||||
id: id,
|
||||
mute: mute
|
||||
}
|
||||
}
|
||||
|
||||
export function setGroupVolume(id, percent, old_percent = 0){
|
||||
return {
|
||||
type: 'SNAPCAST_SET_GROUP_VOLUME',
|
||||
id: id,
|
||||
percent: percent,
|
||||
old_percent: old_percent
|
||||
}
|
||||
}
|
||||
|
||||
381
src/js/services/snapcast/middleware.js
Executable file
381
src/js/services/snapcast/middleware.js
Executable file
@ -0,0 +1,381 @@
|
||||
|
||||
import ReactGA from 'react-ga'
|
||||
|
||||
var helpers = require('../../helpers')
|
||||
var coreActions = require('../core/actions')
|
||||
var uiActions = require('../ui/actions')
|
||||
var pusherActions = require('../pusher/actions')
|
||||
|
||||
const SnapcastMiddleware = (function(){
|
||||
|
||||
const request = (store, params = null) => {
|
||||
store.dispatch(pusherActions.instruct('snapcast_instruct', params));
|
||||
return;
|
||||
}
|
||||
|
||||
return store => next => action => {
|
||||
switch(action.type){
|
||||
|
||||
case 'SNAPCAST_GET_SERVER':
|
||||
console.log( request(store, {method: 'Server.GetStatus'}) )/*
|
||||
.then(
|
||||
response => {
|
||||
store.dispatch(snapcastActions.serverLoaded(response.server));
|
||||
},
|
||||
error => {
|
||||
store.dispatch(coreActions.handleException(
|
||||
'Could not get Snapcast server',
|
||||
error,
|
||||
error.message
|
||||
));
|
||||
}
|
||||
);*/
|
||||
break
|
||||
|
||||
case 'SNAPCAST_SERVER_LOADED':
|
||||
var groups = {};
|
||||
var clients = {};
|
||||
var streams = {};
|
||||
|
||||
// Loop all the groups
|
||||
for (var i = 0; i < action.server.groups.length; i++){
|
||||
var group = action.server.groups[i];
|
||||
var clients_ids = [];
|
||||
|
||||
// And now this groups' clients
|
||||
for (var j = 0; j < group.clients.length; j++){
|
||||
var client = group.clients[j];
|
||||
clients[client.id] = client;
|
||||
clients_ids.push(client.id);
|
||||
}
|
||||
|
||||
groups[group.id] = {
|
||||
id: group.id,
|
||||
muted: group.muted,
|
||||
name: group.name,
|
||||
stream_id: group.stream_id,
|
||||
clients_ids: clients_ids
|
||||
}
|
||||
}
|
||||
|
||||
// Loop all the streams
|
||||
for (var i = 0; i < action.server.streams.length; i++){
|
||||
var stream = action.server.streams[i];
|
||||
streams[stream.id] = stream;
|
||||
}
|
||||
|
||||
action.clients = clients;
|
||||
action.groups = groups;
|
||||
action.streams = streams;
|
||||
next(action);
|
||||
break
|
||||
|
||||
case 'SNAPCAST_SET_CLIENT_NAME':
|
||||
var client = store.getState().snapcast.clients[action.id];
|
||||
var data = {
|
||||
method: 'Client.SetName',
|
||||
params: {
|
||||
id: action.id,
|
||||
name: action.name
|
||||
}
|
||||
}
|
||||
|
||||
request(store, data)
|
||||
.then(
|
||||
response => {
|
||||
store.dispatch({
|
||||
type: 'SNAPCAST_CLIENT_UPDATED',
|
||||
key: action.id,
|
||||
client: {
|
||||
config: {
|
||||
name: response.name
|
||||
}
|
||||
}
|
||||
})
|
||||
},
|
||||
error => {
|
||||
store.dispatch(coreActions.handleException(
|
||||
'Error',
|
||||
error,
|
||||
error.message
|
||||
));
|
||||
}
|
||||
);
|
||||
break
|
||||
|
||||
case 'SNAPCAST_SET_CLIENT_MUTE':
|
||||
var client = store.getState().snapcast.clients[action.id];
|
||||
var data = {
|
||||
method: 'Client.SetVolume',
|
||||
params: {
|
||||
id: action.id,
|
||||
volume: {
|
||||
muted: action.mute,
|
||||
percent: client.config.volume.percent,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
request(store, data)
|
||||
.then(
|
||||
response => {
|
||||
store.dispatch({
|
||||
type: 'SNAPCAST_CLIENT_UPDATED',
|
||||
key: action.id,
|
||||
client: {
|
||||
config: {
|
||||
volume: response.volume
|
||||
}
|
||||
}
|
||||
})
|
||||
},
|
||||
error => {
|
||||
store.dispatch(coreActions.handleException(
|
||||
'Error',
|
||||
error,
|
||||
error.message
|
||||
));
|
||||
}
|
||||
);
|
||||
break
|
||||
|
||||
case 'SNAPCAST_SET_CLIENT_VOLUME':
|
||||
var client = store.getState().snapcast.clients[action.id];
|
||||
var data = {
|
||||
method: 'Client.SetVolume',
|
||||
params: {
|
||||
id: action.id,
|
||||
volume: {
|
||||
muted: client.config.volume.muted,
|
||||
percent: action.percent
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
request(store, data)
|
||||
.then(
|
||||
response => {
|
||||
store.dispatch({
|
||||
type: 'SNAPCAST_CLIENT_UPDATED',
|
||||
key: action.id,
|
||||
client: {
|
||||
config: {
|
||||
volume: response.volume
|
||||
}
|
||||
}
|
||||
})
|
||||
},
|
||||
error => {
|
||||
store.dispatch(coreActions.handleException(
|
||||
'Error',
|
||||
error,
|
||||
error.message
|
||||
));
|
||||
}
|
||||
);
|
||||
break
|
||||
|
||||
case 'SNAPCAST_SET_CLIENT_LATENCY':
|
||||
var client = store.getState().snapcast.clients[action.id];
|
||||
var data = {
|
||||
method: 'Client.SetLatency',
|
||||
params: {
|
||||
id: action.id,
|
||||
latency: action.latency
|
||||
}
|
||||
}
|
||||
|
||||
request(store, data)
|
||||
.then(
|
||||
response => {
|
||||
store.dispatch({
|
||||
type: 'SNAPCAST_CLIENT_UPDATED',
|
||||
key: action.id,
|
||||
client: {
|
||||
config: {
|
||||
latency: response.latency
|
||||
}
|
||||
}
|
||||
})
|
||||
},
|
||||
error => {
|
||||
store.dispatch(coreActions.handleException(
|
||||
'Error',
|
||||
error,
|
||||
error.message
|
||||
));
|
||||
}
|
||||
);
|
||||
break
|
||||
|
||||
case 'SNAPCAST_SET_CLIENT_GROUP':
|
||||
|
||||
var group = store.getState().snapcast.groups[action.group_id];
|
||||
var clients_ids = group.clients_ids;
|
||||
var clients_ids_index = clients_ids.indexOf(action.id);
|
||||
|
||||
// Not in group (yet), so add it
|
||||
if (clients_ids_index <= -1){
|
||||
clients_ids.push(action.id);
|
||||
|
||||
// Already there, so remove it
|
||||
} else {
|
||||
clients_ids.splice(clients_ids_index, 1);
|
||||
}
|
||||
|
||||
var data = {
|
||||
method: 'Group.SetClients',
|
||||
params: {
|
||||
id: action.group_id,
|
||||
clients: clients_ids
|
||||
}
|
||||
}
|
||||
|
||||
request(store, data)
|
||||
.then(
|
||||
response => {
|
||||
store.dispatch(snapcastActions.serverLoaded(response.server));
|
||||
},
|
||||
error => {
|
||||
store.dispatch(coreActions.handleException(
|
||||
'Error',
|
||||
error,
|
||||
error.message
|
||||
));
|
||||
}
|
||||
);
|
||||
break
|
||||
|
||||
case 'SNAPCAST_DELETE_CLIENT':
|
||||
var data = {
|
||||
method: 'Server.DeleteClient',
|
||||
params: {
|
||||
id: action.id
|
||||
}
|
||||
}
|
||||
|
||||
request(store, data)
|
||||
.then(
|
||||
response => {
|
||||
store.dispatch({
|
||||
type: 'SNAPCAST_CLIENT_REMOVED',
|
||||
key: action.data.params.id
|
||||
})
|
||||
},
|
||||
error => {
|
||||
store.dispatch(coreActions.handleException(
|
||||
'Error',
|
||||
error,
|
||||
error.message
|
||||
));
|
||||
}
|
||||
);
|
||||
break
|
||||
|
||||
case 'SNAPCAST_SET_GROUP_STREAM':
|
||||
var group = store.getState().snapcast.groups[action.id];
|
||||
var data = {
|
||||
method: 'Group.SetStream',
|
||||
params: {
|
||||
id: action.id,
|
||||
stream_id: action.stream_id
|
||||
}
|
||||
}
|
||||
|
||||
request(store, data)
|
||||
.then(
|
||||
response => {
|
||||
store.dispatch({
|
||||
type: 'SNAPCAST_GROUP_UPDATED',
|
||||
key: action.id,
|
||||
group: {
|
||||
stream_id: action.stream_id
|
||||
}
|
||||
})
|
||||
},
|
||||
error => {
|
||||
store.dispatch(coreActions.handleException(
|
||||
'Could not change stream',
|
||||
error,
|
||||
error.message
|
||||
));
|
||||
}
|
||||
);
|
||||
break
|
||||
|
||||
case 'SNAPCAST_SET_GROUP_MUTE':
|
||||
var group = store.getState().snapcast.groups[action.id];
|
||||
var data = {
|
||||
method: 'Group.SetMute',
|
||||
params: {
|
||||
id: action.id,
|
||||
mute: action.mute
|
||||
}
|
||||
}
|
||||
|
||||
request(store, data)
|
||||
.then(
|
||||
response => {
|
||||
store.dispatch({
|
||||
type: 'SNAPCAST_GROUP_UPDATED',
|
||||
key: action.id,
|
||||
group: {
|
||||
muted: response.mute
|
||||
}
|
||||
})
|
||||
},
|
||||
error => {
|
||||
store.dispatch(coreActions.handleException(
|
||||
'Could not toggle mute',
|
||||
error,
|
||||
error.message
|
||||
));
|
||||
}
|
||||
);
|
||||
break
|
||||
|
||||
case 'SNAPCAST_SET_GROUP_VOLUME':
|
||||
var clients_to_update = [];
|
||||
var group = store.getState().snapcast.groups[action.id];
|
||||
var change = action.percent - action.old_percent;
|
||||
|
||||
for (var i = 0; i < group.clients_ids.length; i++){
|
||||
|
||||
// Apply the change proportionately to each client
|
||||
var client = store.getState().snapcast.clients[group.clients_ids[i]];
|
||||
var current_percent = client.config.volume.percent;
|
||||
var new_percent = current_percent + change;
|
||||
|
||||
// Only change if the client is within min/max limits
|
||||
if ((change > 0 && current_percent < 100) || (change < 0 && current_percent > 0)){
|
||||
clients_to_update.push({
|
||||
id: client.id,
|
||||
percent: new_percent
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
// Loop our required changes, and post each to Snapcast
|
||||
for (var i = 0; i < clients_to_update.length; i++){
|
||||
var update = clients_to_update[i];
|
||||
var percent = update.percent + ((group.clients_ids.length - clients_to_update.length) * change);
|
||||
|
||||
// Make sure we're not creating an impossible percent
|
||||
if (percent < 0){
|
||||
percent = 0;
|
||||
} else if (percent > 100){
|
||||
percent = 100;
|
||||
}
|
||||
|
||||
store.dispatch(snapcastActions.setClientVolume(update.id, percent));
|
||||
}
|
||||
|
||||
// This action is irrelevant to us, pass it on to the next middleware
|
||||
default:
|
||||
return next(action);
|
||||
}
|
||||
}
|
||||
|
||||
})();
|
||||
|
||||
export default SnapcastMiddleware
|
||||
36
src/js/services/snapcast/reducer.js
Executable file
36
src/js/services/snapcast/reducer.js
Executable file
@ -0,0 +1,36 @@
|
||||
|
||||
export default function reducer(snapcast = {}, action){
|
||||
switch (action.type){
|
||||
|
||||
case 'SNAPCAST_SERVER_LOADED':
|
||||
return Object.assign({}, snapcast, {
|
||||
streams: action.streams,
|
||||
groups: action.groups,
|
||||
clients: action.clients
|
||||
});
|
||||
|
||||
case 'SNAPCAST_GROUP_UPDATED':
|
||||
var groups = Object.assign({}, snapcast.groups);
|
||||
var group = groups[action.key];
|
||||
groups[action.key] = Object.assign({}, group, action.group);
|
||||
return Object.assign({}, snapcast, { groups: groups });
|
||||
|
||||
case 'SNAPCAST_CLIENT_UPDATED':
|
||||
var clients = Object.assign({}, snapcast.clients);
|
||||
var client = clients[action.key];
|
||||
client.config = Object.assign({}, client.config, action.client.config);
|
||||
clients[action.key] = client;
|
||||
return Object.assign({}, snapcast, { clients: clients });
|
||||
|
||||
case 'SNAPCAST_CLIENT_REMOVED':
|
||||
var clients = Object.assign({}, snapcast.clients);
|
||||
delete clients[action.key];
|
||||
return Object.assign({}, snapcast, { clients: clients });
|
||||
|
||||
default:
|
||||
return snapcast
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
Reference in New Issue
Block a user