initial commit
commit
ff6d8ac971
@ -0,0 +1,2 @@
|
||||
node_modules
|
||||
package-lock.json
|
@ -0,0 +1,59 @@
|
||||
{
|
||||
"pager": {
|
||||
"url": "http://127.0.0.1:3000/api/message/advanced",
|
||||
"params": {
|
||||
"type": "simple",
|
||||
"routing": {
|
||||
"device": "generic",
|
||||
"connectors": [
|
||||
[
|
||||
"dummy",
|
||||
"1234567"
|
||||
]
|
||||
]
|
||||
}
|
||||
}
|
||||
},
|
||||
"germanUmlautSupport": true,
|
||||
"regions": [
|
||||
{
|
||||
"name": "MD",
|
||||
"active": true,
|
||||
"params": {
|
||||
"type": "duplex",
|
||||
"routing": {
|
||||
"device": "birdyslim",
|
||||
"connectors": [
|
||||
[
|
||||
"dummy",
|
||||
"123456"
|
||||
]
|
||||
]
|
||||
}
|
||||
},
|
||||
"regionsID": "150030000000",
|
||||
"mowas": true,
|
||||
"preset": "184cd5772a5"
|
||||
},
|
||||
{
|
||||
"name": "Pyrbaum",
|
||||
"params": {
|
||||
"type": "simple",
|
||||
"routing": {
|
||||
"device": "generic",
|
||||
"connectors": [
|
||||
[
|
||||
"dummy",
|
||||
"444"
|
||||
]
|
||||
]
|
||||
}
|
||||
},
|
||||
"alarmSchedulingMode": "weekly",
|
||||
"active": true,
|
||||
"mowas": true,
|
||||
"regionsID": "093730156156",
|
||||
"preset": "184cd5772a5"
|
||||
}
|
||||
]
|
||||
}
|
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
@ -0,0 +1,197 @@
|
||||
<!DOCTYPE html>
|
||||
<html>
|
||||
|
||||
<head>
|
||||
<title>Nina Configuration</title>
|
||||
<!-- <link href="https://fonts.googleapis.com/css?family=Roboto:100,300,400,500,700,900" rel="stylesheet"> -->
|
||||
<link href="css/materialdesignicons.min.css" rel="stylesheet">
|
||||
<link href="css/vuetify.min.css" rel="stylesheet">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=1, user-scalable=no, minimal-ui">
|
||||
</head>
|
||||
|
||||
<body>
|
||||
<div id="app">
|
||||
<v-app>
|
||||
<v-app-bar app>
|
||||
<v-toolbar-title>MOWAS/BBK Configuration</v-toolbar-title>
|
||||
<v-spacer></v-spacer>
|
||||
<v-btn color="success" @click="storeConfig()">Store & Restart</v-btn>
|
||||
<v-checkbox label="Expert Mode" v-model="EXPERTMODE"></v-checkbox>
|
||||
</v-app-bar>
|
||||
<v-content>
|
||||
<v-form>
|
||||
<v-tabs v-model="configTab" next-icon="mdi-arrow-right-bold-box-outline"
|
||||
prev-icon="mdi-arrow-left-bold-box-outline" show-arrows>
|
||||
<v-tabs-slider></v-tabs-slider>
|
||||
<v-tab key="regions">Regions</v-tab>
|
||||
<v-tab v-show="EXPERTMODE" key="notificationConfig">Notification Configuration</v-tab>
|
||||
</v-tabs>
|
||||
<v-tabs-items v-model="configTab">
|
||||
<v-tab-item key="regions">
|
||||
<v-container>
|
||||
<v-row>
|
||||
<v-checkbox label="use German POCSAG" v-model="configData.germanUmlautSupport"></v-checkbox>
|
||||
</v-row>
|
||||
<v-row>
|
||||
<h3>Regions:</h3>
|
||||
</v-row>
|
||||
<v-row>
|
||||
<v-btn color="primary" fab dark small icon @click="addAlarm()">
|
||||
<v-icon>mdi-plus</v-icon>
|
||||
</v-btn>
|
||||
</v-row>
|
||||
|
||||
<v-row v-for="(subConfig, index) in configData.regions" :key="subConfig._id">
|
||||
<v-col cols="4">
|
||||
<v-checkbox label="Active" v-model="subConfig.active"></v-checkbox>
|
||||
<v-text-field v-model="subConfig.name" label="Name"></v-text-field>
|
||||
<v-autocomplete
|
||||
v-model="subConfig.preset"
|
||||
:items="presetSearchItems"
|
||||
:loading="!presetSearchItems.length > 0"
|
||||
color="white"
|
||||
hide-no-data
|
||||
dense
|
||||
label="Profile"
|
||||
placeholder="Start typing to Search"
|
||||
prepend-icon="mdi-database-search"
|
||||
></v-autocomplete>
|
||||
</v-col>
|
||||
<v-col cols="6">
|
||||
<v-autocomplete
|
||||
v-model="subConfig.regionsID"
|
||||
:items="regionsSearchItems"
|
||||
:loading="!regionsSearchItems.length > 0"
|
||||
color="white"
|
||||
hide-no-data
|
||||
dense
|
||||
label="Amtlicher Regionalschlüssel"
|
||||
placeholder="Start typing to Search"
|
||||
prepend-icon="mdi-database-search"
|
||||
></v-autocomplete>
|
||||
<v-checkbox v-model="subConfig.mowas" label="MOWAS"></v-checkbox>
|
||||
</v-col>
|
||||
<v-col cols="2">
|
||||
<v-btn color="error" @click="configData.regions.splice(index, 1)" fab icon><v-icon>mdi-delete</v-icon></v-btn>
|
||||
</v-col>
|
||||
<v-col cols="12">
|
||||
<hr/>
|
||||
</v-col>
|
||||
</v-row>
|
||||
</v-container>
|
||||
</v-tab-item>
|
||||
<v-tab-item key="notificationConfig">
|
||||
<v-container>
|
||||
<b>Routing Paramters:</b>
|
||||
<v-row>
|
||||
<v-col cols="12" sm="12" md="6">
|
||||
<v-text-field label="Daemon Endpoint URL" v-model="configData.pager.url">
|
||||
</v-text-field>
|
||||
</v-col>
|
||||
</v-row>
|
||||
</v-container>
|
||||
</v-tab-item>
|
||||
</v-tabs-items>
|
||||
</v-form>
|
||||
</v-content>
|
||||
</v-app>
|
||||
</div>
|
||||
|
||||
<script src="js/vue/vue.js"></script>
|
||||
<script src="js/vue/vuetify.js"></script>
|
||||
<script src="js/vue/vue-resource_1.5.1.js"></script>
|
||||
<script>
|
||||
new Vue({
|
||||
el: '#app',
|
||||
vuetify: new Vuetify(),
|
||||
http: { root: '/' },
|
||||
data() {
|
||||
return {
|
||||
EXPERTMODE: false,
|
||||
configTab: null,
|
||||
configData: {
|
||||
"bottoken": "",
|
||||
"pager": {
|
||||
"url": "",
|
||||
},
|
||||
"germanUmlautSupport": false,
|
||||
"regions": []
|
||||
},
|
||||
|
||||
regionsSearchItems: [],
|
||||
presetSearchItems: [],
|
||||
}
|
||||
},
|
||||
created() {
|
||||
this.loadPresets()
|
||||
this.loadConfig()
|
||||
},
|
||||
methods: {
|
||||
loadPresets() {
|
||||
this.$http.get('/api/deliveryPresets')
|
||||
.then(response => {
|
||||
this.presetSearchItems = response.body.map(x => { return {
|
||||
text: x.name,
|
||||
value: x.key,
|
||||
}})
|
||||
}, response => {
|
||||
})
|
||||
},
|
||||
loadConfig() {
|
||||
this.$http.get('/config').then(response => {
|
||||
const newConfig = response.body
|
||||
newConfig.regions = newConfig.regions.map((x) => {
|
||||
x._id = btoa(JSON.stringify(x))
|
||||
return x
|
||||
})
|
||||
this.configData = newConfig
|
||||
}, response => {
|
||||
})
|
||||
|
||||
this.$http.get('/Regionalschl_ssel_2022-09-30.json').then(response => {
|
||||
|
||||
console.log(response.body.daten)
|
||||
this.regionsSearchItems = response.body.daten.map(x => { return {
|
||||
text: (!!x[2] ? x.slice(1).join(' - ') : x[1]) + ' (' + x[0] + ')',
|
||||
value: x[0],
|
||||
//Description
|
||||
}})
|
||||
}, response => {
|
||||
})
|
||||
},
|
||||
storeConfig() {
|
||||
const storeConfig = JSON.parse(JSON.stringify(this.configData))
|
||||
storeConfig.regions = storeConfig.regions.map(a=> {
|
||||
delete a._id
|
||||
return a
|
||||
})
|
||||
this.$http.post('/config', storeConfig).then(response => {
|
||||
})
|
||||
.then(this.$http.post('/restart'))
|
||||
.then(() => {
|
||||
document.body.style = 'display:none'
|
||||
setTimeout(() => window.location.reload(), 1e3)
|
||||
})
|
||||
},
|
||||
addDeliveryTarget(index) {
|
||||
this.configData.regions[index].params.routing.connectors.push(["connectorName", "connectorParam"])
|
||||
},
|
||||
addAlarm() {
|
||||
this.configData.regions.push({
|
||||
name: "Testalarm",
|
||||
params: {
|
||||
"type": "simple",
|
||||
"routing": {
|
||||
"device": "generic",
|
||||
"connectors": []
|
||||
},
|
||||
},
|
||||
"alarmSchedulingMode": "weekly",
|
||||
})
|
||||
},
|
||||
}
|
||||
})
|
||||
</script>
|
||||
</body>
|
||||
|
||||
</html>
|
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
@ -0,0 +1,137 @@
|
||||
const fs = require('fs')
|
||||
|
||||
const config = require('./config.json')
|
||||
|
||||
const axios = require('axios')
|
||||
|
||||
const stateMachine = {}
|
||||
const umlautMapGermany = {
|
||||
'\u00dc': ']',
|
||||
'\u00c4': '[',
|
||||
'\u00d6': "\\",
|
||||
'\u00fc': '}',
|
||||
'\u00e4': '{',
|
||||
'\u00f6': '|',
|
||||
'\u00df': '~',
|
||||
}
|
||||
const umlautMapIntl = {
|
||||
'\u00dc': 'U',
|
||||
'\u00c4': 'A',
|
||||
'\u00d6': 'O',
|
||||
'\u00fc': 'u',
|
||||
'\u00e4': 'a',
|
||||
'\u00f6': 'o',
|
||||
'\u00df': 'ss',
|
||||
}
|
||||
|
||||
function replaceUmlaute(str) {
|
||||
let umlautMap = config.germanUmlautSupport
|
||||
? umlautMapGermany
|
||||
: umlautMapIntl
|
||||
return str
|
||||
.replace(/[\u00dc|\u00c4|\u00d6][a-z]/g, (a) => {
|
||||
const big = umlautMap[a.slice(0, 1)];
|
||||
return big.charAt(0) + big.charAt(1).toLowerCase() + a.slice(1);
|
||||
})
|
||||
.replace(new RegExp('['+Object.keys(umlautMap).join('|')+']',"g"),
|
||||
(a) => umlautMap[a]
|
||||
);
|
||||
}
|
||||
|
||||
async function sendPage(preset, payload) {
|
||||
console.log(preset, payload)
|
||||
try {
|
||||
await axios.post(new URL(config.pager.url).origin + '/api/message/' + (!!preset ? 'preset' : 'advanced'), Object.assign( !!preset
|
||||
? { preset }
|
||||
: { ...config.pager.params } // backward compatibility
|
||||
, { payload: payload }))
|
||||
} catch (e) {}
|
||||
}
|
||||
|
||||
async function checkWOMAS() {
|
||||
for (region of config.regions) {
|
||||
if (!region.active) continue
|
||||
try {
|
||||
const regId = region.regionsID.substring(0, 6) + '000000'
|
||||
let dashboardData = await axios.get('https://warnung.bund.de/api31/dashboard/' + regId + '.json')
|
||||
//console.log('dashboardData', dashboardData.data)
|
||||
for (let alarmHead of dashboardData.data) {
|
||||
if (!stateMachine[ alarmHead.id ]) {
|
||||
let alarmData = await axios.get('https://nina.api.proxy.bund.dev/api31/warnings/' + alarmHead.id + '.json')
|
||||
for (let infoData of alarmData.data.info) {
|
||||
await sendPage(region.preset, replaceUmlaute(infoData.headline))
|
||||
}
|
||||
console.log(stateMachine[ alarmHead.id ])
|
||||
stateMachine[ alarmHead.id ] = alarmData.data.info.length
|
||||
}
|
||||
//console.log('alarmData', alarmData.data)
|
||||
}
|
||||
/*let msg = ""
|
||||
rssData.entries.sort((a,b) => new Date(b.published).valueOf() - new Date(a.published).valueOf())
|
||||
if (rssData.entries.length > 0) {
|
||||
msg = rssData.entries[0].description
|
||||
msg = msg.replace('DWD WETTERWARNUNG:', 'DWD:')
|
||||
msg = msg.replace(' in ', ' ')
|
||||
msg = msg.replace(' von ', '/')
|
||||
msg = msg.indexOf('Quelle:') > -1
|
||||
? msg.split('Quelle:')[0]
|
||||
: msg
|
||||
msg = replaceUmlaute(msg)
|
||||
|
||||
if (!!stateMachine[ region.dwdID ]) {
|
||||
if (stateMachine [ region.dwdID ] != msg) {
|
||||
await sendPage( msg )
|
||||
stateMachine [ region.dwdID ] = msg
|
||||
}
|
||||
} else {
|
||||
stateMachine [ region.dwdID ] = msg
|
||||
// if initial state is unknown and we have an alert, send it anyway
|
||||
if (msg.indexOf('Es sind keine Warnungen') == -1) {
|
||||
await sendPage( msg )
|
||||
}
|
||||
}
|
||||
}
|
||||
*/
|
||||
} catch (e) { console.error(e) }
|
||||
}
|
||||
}
|
||||
function main() {
|
||||
// listen
|
||||
setTimeout(checkWOMAS, 0)
|
||||
setInterval(checkWOMAS, 5 * 60 * 1e3)
|
||||
}
|
||||
|
||||
|
||||
main()
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
const express = require('express')
|
||||
const appConfig = express()
|
||||
appConfig.use(express.json())
|
||||
appConfig.use(express.static('html'))
|
||||
appConfig.use(express.static(__dirname + '/node_modules/@mdi/font'))
|
||||
|
||||
/** CONFIG Routes */
|
||||
|
||||
appConfig.get('/config', async (req, res) => {
|
||||
return res.json(JSON.parse(fs.readFileSync('config.json')))
|
||||
})
|
||||
appConfig.get('/api/deliveryPresets', async (req, res) => {
|
||||
const presets = await axios.get(new URL(config.pager.url).origin + '/api/deliveryPresets')
|
||||
return res.json(presets.data)
|
||||
})
|
||||
appConfig.post('/config', async (req, res) => {
|
||||
if (!(!!req.body.pager)) return res.status(403).json(false)
|
||||
if (!(!!req.body.regions)) return res.status(403).json(false)
|
||||
console.log(req.body)
|
||||
fs.writeFileSync('config.json', JSON.stringify(req.body, null, "\t"))
|
||||
return res.json(true)
|
||||
})
|
||||
appConfig.post('/restart', (req, res) => {
|
||||
process.exit(1)
|
||||
})
|
||||
|
||||
appConfig.listen(3090, '0.0.0.0' || config.host || '127.0.0.1')
|
@ -0,0 +1,16 @@
|
||||
{
|
||||
"name": "msg-nina",
|
||||
"version": "1.0.0",
|
||||
"description": "",
|
||||
"main": "index.js",
|
||||
"scripts": {
|
||||
"test": "echo \"Error: no test specified\" && exit 1"
|
||||
},
|
||||
"author": "",
|
||||
"license": "ISC",
|
||||
"dependencies": {
|
||||
"@mdi/font": "^7.0.96",
|
||||
"axios": "^1.2.0",
|
||||
"express": "^4.18.2"
|
||||
}
|
||||
}
|
Loading…
Reference in New Issue