mirror of
https://github.com/wkulhanek/bonob.git
synced 2025-12-22 09:53:32 +01:00
Add simple listing of sonos devices and basic Dockefile for running it
This commit is contained in:
5
.gitignore
vendored
5
.gitignore
vendored
@@ -1,3 +1,4 @@
|
|||||||
.vscode
|
|
||||||
node_modules
|
|
||||||
.nyc_output
|
.nyc_output
|
||||||
|
.vscode
|
||||||
|
build
|
||||||
|
node_modules
|
||||||
|
|||||||
28
Dockerfile
Normal file
28
Dockerfile
Normal file
@@ -0,0 +1,28 @@
|
|||||||
|
FROM node:14.15-alpine as build
|
||||||
|
|
||||||
|
WORKDIR /bonob
|
||||||
|
|
||||||
|
COPY package.json .
|
||||||
|
COPY yarn.lock .
|
||||||
|
COPY tsconfig.json .
|
||||||
|
COPY src .
|
||||||
|
|
||||||
|
RUN yarn install && \
|
||||||
|
yarn build
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
FROM node:14.15-alpine
|
||||||
|
|
||||||
|
EXPOSE 3000
|
||||||
|
|
||||||
|
WORKDIR /bonob
|
||||||
|
|
||||||
|
COPY package.json .
|
||||||
|
COPY yarn.lock .
|
||||||
|
COPY --from=build /bonob/build/* ./
|
||||||
|
COPY web web
|
||||||
|
|
||||||
|
RUN yarn install --prod
|
||||||
|
|
||||||
|
CMD ["node", "./app.js"]
|
||||||
@@ -1,2 +0,0 @@
|
|||||||
"use strict";
|
|
||||||
console.log("Hello World!!!");
|
|
||||||
16
package.json
16
package.json
@@ -6,24 +6,30 @@
|
|||||||
"author": "simojenki <simojenki@users.noreply.github.com>",
|
"author": "simojenki <simojenki@users.noreply.github.com>",
|
||||||
"license": "GPL-3.0-only",
|
"license": "GPL-3.0-only",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
|
"@svrooij/sonos": "^2.3.0",
|
||||||
"@types/express": "^4.17.11",
|
"@types/express": "^4.17.11",
|
||||||
"@types/node": "^14.14.22",
|
"@types/node": "^14.14.22",
|
||||||
|
"axios": "^0.21.1",
|
||||||
|
"eta": "^1.12.1",
|
||||||
"express": "^4.17.1",
|
"express": "^4.17.1",
|
||||||
"typescript": "^4.1.3"
|
"ts-md5": "^1.2.7",
|
||||||
|
"typescript": "^4.1.3",
|
||||||
|
"winston": "^3.3.3"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@types/chai": "^4.2.14",
|
"@types/chai": "^4.2.14",
|
||||||
"@types/mocha": "^8.2.0",
|
"@types/mocha": "^8.2.0",
|
||||||
"chai": "^4.2.0",
|
"chai": "^4.2.0",
|
||||||
"ts-node": "^9.1.1",
|
|
||||||
"mocha": "^8.2.1",
|
"mocha": "^8.2.1",
|
||||||
|
"nodemon": "^2.0.7",
|
||||||
"nyc": "^15.1.0",
|
"nyc": "^15.1.0",
|
||||||
"ts-mockito": "^2.6.1"
|
"ts-mockito": "^2.6.1",
|
||||||
|
"ts-node": "^9.1.1"
|
||||||
},
|
},
|
||||||
"scripts": {
|
"scripts": {
|
||||||
"build": "tsc",
|
"build": "tsc",
|
||||||
"start": "node ./bin/app.js",
|
"start": "node ./build/app.js",
|
||||||
"dev": "ts-node ./src/app.ts",
|
"dev": "nodemon ./src/app.ts",
|
||||||
"test": "nyc ./node_modules/.bin/_mocha 'tests/**/*.test.ts'",
|
"test": "nyc ./node_modules/.bin/_mocha 'tests/**/*.test.ts'",
|
||||||
"testw": "mocha --require ts-node/register --watch --watch-files tests/**/* tests/**/*.test.ts"
|
"testw": "mocha --require ts-node/register --watch --watch-files tests/**/* tests/**/*.test.ts"
|
||||||
}
|
}
|
||||||
|
|||||||
35
src/app.ts
35
src/app.ts
@@ -1,11 +1,26 @@
|
|||||||
import {createServer} from './utils/server'
|
import express from "express";
|
||||||
|
import * as Eta from "eta";
|
||||||
|
|
||||||
createServer()
|
// import { Navidrome } from "./music_service";
|
||||||
.then(server => {
|
import makeSonos from "./sonos";
|
||||||
server.listen(3000, () => {
|
|
||||||
console.info(`Listening on http://localhost:3000`)
|
const PORT = 3000;
|
||||||
})
|
|
||||||
})
|
makeSonos().then((sonos) => {
|
||||||
.catch(err => {
|
const app = express();
|
||||||
console.error(`Error: ${err}`)
|
app.use(express.static("./web/public"));
|
||||||
})
|
|
||||||
|
app.engine("eta", Eta.renderFile);
|
||||||
|
app.set("view engine", "eta");
|
||||||
|
app.set("views", "./web/views");
|
||||||
|
|
||||||
|
app.get("/", (_, res) => {
|
||||||
|
res.render("index", {
|
||||||
|
devices: sonos.devices(),
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
app.listen(PORT, () => {
|
||||||
|
console.info(`Listening on ${PORT}`);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|||||||
24
src/logger.ts
Normal file
24
src/logger.ts
Normal file
@@ -0,0 +1,24 @@
|
|||||||
|
import { createLogger, format, transports } from 'winston';
|
||||||
|
|
||||||
|
export function debugIt<T>(thing: T): T {
|
||||||
|
logger.debug(thing);
|
||||||
|
return thing;
|
||||||
|
}
|
||||||
|
|
||||||
|
const logger = createLogger({
|
||||||
|
level: 'debug',
|
||||||
|
format: format.combine(
|
||||||
|
format.timestamp({
|
||||||
|
format: 'YYYY-MM-DD HH:mm:ss'
|
||||||
|
}),
|
||||||
|
format.errors({ stack: true }),
|
||||||
|
format.splat(),
|
||||||
|
format.json()
|
||||||
|
),
|
||||||
|
defaultMeta: { service: 'bonob' },
|
||||||
|
transports: [
|
||||||
|
new transports.Console()
|
||||||
|
]
|
||||||
|
});
|
||||||
|
|
||||||
|
export default logger;
|
||||||
20
src/music_service.ts
Normal file
20
src/music_service.ts
Normal file
@@ -0,0 +1,20 @@
|
|||||||
|
import axios from "axios";
|
||||||
|
import { Md5 } from "ts-md5/dist/md5";
|
||||||
|
|
||||||
|
const s = "foobar100";
|
||||||
|
const navidrome = process.env["BONOB_NAVIDROME_URL"];
|
||||||
|
const u = process.env["BONOB_USER"];
|
||||||
|
const t = Md5.hashStr(`${process.env["BONOB_PASSWORD"]}${s}`);
|
||||||
|
|
||||||
|
export class Navidrome {
|
||||||
|
ping = (): Promise<boolean> =>
|
||||||
|
axios
|
||||||
|
.get(
|
||||||
|
`${navidrome}/rest/ping.view?u=${u}&t=${t}&s=${s}&v=1.16.1.0&c=myapp`
|
||||||
|
)
|
||||||
|
.then((_) => true)
|
||||||
|
.catch((e) => {
|
||||||
|
console.log(e);
|
||||||
|
return false;
|
||||||
|
});
|
||||||
|
}
|
||||||
48
src/sonos.ts
Normal file
48
src/sonos.ts
Normal file
@@ -0,0 +1,48 @@
|
|||||||
|
import { SonosManager } from "@svrooij/sonos";
|
||||||
|
|
||||||
|
import logger from "./logger";
|
||||||
|
|
||||||
|
type Device = {
|
||||||
|
name: string;
|
||||||
|
group: string;
|
||||||
|
ip: string;
|
||||||
|
port: number;
|
||||||
|
};
|
||||||
|
|
||||||
|
interface Sonos
|
||||||
|
{
|
||||||
|
devices: () => Device[];
|
||||||
|
}
|
||||||
|
|
||||||
|
class RealSonos implements Sonos {
|
||||||
|
manager: SonosManager;
|
||||||
|
|
||||||
|
constructor(manager: SonosManager) {
|
||||||
|
this.manager = manager;
|
||||||
|
}
|
||||||
|
|
||||||
|
devices = (): Device[] => {
|
||||||
|
const devices = this.manager.Devices.map((d) => ({
|
||||||
|
name: d.Name,
|
||||||
|
group: d.GroupName || "",
|
||||||
|
ip: d.Host,
|
||||||
|
port: d.Port,
|
||||||
|
}));
|
||||||
|
logger.debug({ devices })
|
||||||
|
return devices;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const SonosDisabled: Sonos = {
|
||||||
|
devices: () => [],
|
||||||
|
};
|
||||||
|
|
||||||
|
export default function (): Promise<Sonos> {
|
||||||
|
const manager = new SonosManager();
|
||||||
|
return manager
|
||||||
|
.InitializeWithDiscovery(10)
|
||||||
|
.then((it) => (it ? new RealSonos(manager) : SonosDisabled))
|
||||||
|
.catch((_) => {
|
||||||
|
return SonosDisabled;
|
||||||
|
});
|
||||||
|
}
|
||||||
@@ -1,10 +0,0 @@
|
|||||||
import express from 'express'
|
|
||||||
import {Express} from 'express-serve-static-core'
|
|
||||||
|
|
||||||
export async function createServer(): Promise<Express> {
|
|
||||||
const server = express()
|
|
||||||
server.get('/', (_, res) => {
|
|
||||||
res.send('Hello world!!!')
|
|
||||||
})
|
|
||||||
return server
|
|
||||||
}
|
|
||||||
@@ -4,7 +4,7 @@
|
|||||||
|
|
||||||
/* Basic Options */
|
/* Basic Options */
|
||||||
// "incremental": true, /* Enable incremental compilation */
|
// "incremental": true, /* Enable incremental compilation */
|
||||||
"target": "es5", /* Specify ECMAScript target version: 'ES3' (default), 'ES5', 'ES2015', 'ES2016', 'ES2017', 'ES2018', 'ES2019', 'ES2020', or 'ESNEXT'. */
|
"target": "ES6", /* Specify ECMAScript target version: 'ES3' (default), 'ES5', 'ES2015', 'ES2016', 'ES2017', 'ES2018', 'ES2019', 'ES2020', or 'ESNEXT'. */
|
||||||
"module": "commonjs", /* Specify module code generation: 'none', 'commonjs', 'amd', 'system', 'umd', 'es2015', 'es2020', or 'ESNext'. */
|
"module": "commonjs", /* Specify module code generation: 'none', 'commonjs', 'amd', 'system', 'umd', 'es2015', 'es2020', or 'ESNext'. */
|
||||||
"lib": ["es2019"], /* Specify library files to be included in the compilation. */
|
"lib": ["es2019"], /* Specify library files to be included in the compilation. */
|
||||||
// "allowJs": true, /* Allow javascript files to be compiled. */
|
// "allowJs": true, /* Allow javascript files to be compiled. */
|
||||||
@@ -14,7 +14,7 @@
|
|||||||
// "declarationMap": true, /* Generates a sourcemap for each corresponding '.d.ts' file. */
|
// "declarationMap": true, /* Generates a sourcemap for each corresponding '.d.ts' file. */
|
||||||
// "sourceMap": true, /* Generates corresponding '.map' file. */
|
// "sourceMap": true, /* Generates corresponding '.map' file. */
|
||||||
// "outFile": "./", /* Concatenate and emit output to single file. */
|
// "outFile": "./", /* Concatenate and emit output to single file. */
|
||||||
"outDir": "./bin", /* Redirect output structure to the directory. */
|
"outDir": "./build", /* Redirect output structure to the directory. */
|
||||||
"rootDir": ".", /* Specify the root directory of input files. Use to control the output directory structure with --outDir. */
|
"rootDir": ".", /* Specify the root directory of input files. Use to control the output directory structure with --outDir. */
|
||||||
// "composite": true, /* Enable project compilation */
|
// "composite": true, /* Enable project compilation */
|
||||||
// "tsBuildInfoFile": "./", /* Specify file to store incremental compilation information */
|
// "tsBuildInfoFile": "./", /* Specify file to store incremental compilation information */
|
||||||
|
|||||||
6
web/views/devices.eta
Normal file
6
web/views/devices.eta
Normal file
@@ -0,0 +1,6 @@
|
|||||||
|
<ul>
|
||||||
|
<% it.devices.forEach(function(d){ %>
|
||||||
|
<li><%= d.name %> (<%= d.ip %>:<%= d.port %>)</li>
|
||||||
|
<% }) %>
|
||||||
|
</ul>
|
||||||
|
|
||||||
11
web/views/index.eta
Normal file
11
web/views/index.eta
Normal file
@@ -0,0 +1,11 @@
|
|||||||
|
<% layout('./layout') %>
|
||||||
|
|
||||||
|
<div id="content">
|
||||||
|
<h1>bonob</h1>
|
||||||
|
<h2>Devices</h2>
|
||||||
|
<ul>
|
||||||
|
<% it.devices.forEach(function(d){ %>
|
||||||
|
<li><%= d.name %> (<%= d.ip %>:<%= d.port %>)</li>
|
||||||
|
<% }) %>
|
||||||
|
</ul>
|
||||||
|
</div>
|
||||||
9
web/views/layout.eta
Normal file
9
web/views/layout.eta
Normal file
@@ -0,0 +1,9 @@
|
|||||||
|
<!DOCTYPE html>
|
||||||
|
<html lang="en">
|
||||||
|
<head>
|
||||||
|
<title><%= it.title || "bonob" %></title>
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
<%~ it.body %>
|
||||||
|
</body>
|
||||||
|
</html>
|
||||||
Reference in New Issue
Block a user