initial commit

master
cheetah 2 years ago
commit ff6d8ac971

2
.gitignore vendored

@ -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…
Cancel
Save