first commit
commit
8967f6c097
@ -0,0 +1,23 @@
|
||||
{
|
||||
"pager": {
|
||||
"url": "http://127.0.0.1:3000/api/message/advanced"
|
||||
},
|
||||
"alarms": [
|
||||
{
|
||||
"name": "Testalarm",
|
||||
"preset": "18ccbeedf26",
|
||||
"alarmSchedulingMode": "weekly",
|
||||
"alarmTime": "12:05",
|
||||
"callsign": "EDDM",
|
||||
"weekDay": {
|
||||
"0": false,
|
||||
"1": false,
|
||||
"2": false,
|
||||
"3": false,
|
||||
"4": true,
|
||||
"5": false,
|
||||
"6": true
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
File diff suppressed because one or more lines are too long
@ -0,0 +1,204 @@
|
||||
<!DOCTYPE html>
|
||||
<html>
|
||||
|
||||
<head>
|
||||
<title>METAR 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>METAR 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="testAlarms">METAR Notifications</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="testAlarms">
|
||||
<v-container>
|
||||
<p>Targets:</p>
|
||||
<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="(alarmConfig, index) in configData.alarms" :key="alarmConfig._id">
|
||||
<v-card>
|
||||
<v-col cols="12" sm="12" md="12">
|
||||
<v-btn color="error" dark small fab @click="configData.alarms.splice(index, 1)" icon>
|
||||
<v-icon>mdi-delete</v-icon>
|
||||
</v-btn>
|
||||
</v-col>
|
||||
<v-col cols="12" sm="12" md="12">
|
||||
<v-col>
|
||||
<v-row>
|
||||
<v-select :items="alarmSchedulingMode" v-model="alarmConfig.alarmSchedulingMode"
|
||||
item-text="k" item-value="v" label="Scheduling Mode"></v-select>
|
||||
<v-text-field v-model="alarmConfig.alarmTime" type="time" label="Time"></v-text-field>
|
||||
</v-row>
|
||||
<v-row cols="3" sm="3" md="3" v-show="alarmConfig.alarmSchedulingMode == 'weekly'">
|
||||
<v-checkbox v-for="(WN, index) of weekDays" v-model="alarmConfig.weekDay[ index ]" :label="WN"></v-checkbox>
|
||||
</v-row>
|
||||
<v-row cols="3" sm="3" md="3" v-show="alarmConfig.alarmSchedulingMode == 'monthlyFirstOccWeekday'">
|
||||
<v-select :items="weekDaysSelect" v-model="alarmConfig.firstOccWeekday" label="First Occurance of this Weekday"></v-select>
|
||||
</v-row>
|
||||
<v-row cols="3" sm="3" md="3" v-show="alarmConfig.alarmSchedulingMode == 'monthlyAtSpecificDate'">
|
||||
<v-text-field v-model="alarmConfig.specificWeekday" label="Specific Date (xx-MM-YYYY)"></v-text-field>
|
||||
</v-row>
|
||||
</v-col>
|
||||
<hr/>
|
||||
</v-col>
|
||||
<v-row>
|
||||
<v-col cols="6" sm="6" md="6">
|
||||
<v-text-field v-model="alarmConfig.name" label="Name"></v-text-field>
|
||||
<v-autocomplete
|
||||
v-model="alarmConfig.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" sm="6" md="6">
|
||||
<v-text-field v-model="alarmConfig.callsign" label="CALLSIGN"></v-text-field>
|
||||
</v-col>
|
||||
<v-col>
|
||||
</v-col>
|
||||
</v-row>
|
||||
</v-card>
|
||||
</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/moment-with-locales.min.js"></script>
|
||||
<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() {
|
||||
const weekDays = [
|
||||
'Monday','Tuesday','Wednesday','Thursday','Friday','Saturday', 'Sunday'
|
||||
]
|
||||
return {
|
||||
EXPERTMODE: false,
|
||||
configTab: null,
|
||||
alarmSchedulingMode: [
|
||||
{ k: 'Weekly', v: 'weekly' },
|
||||
{ k: 'Monthly at first occurance of a specific weekday', v: 'monthlyFirstOccWeekday' },
|
||||
{ k: 'Monthly at specific date', v: 'monthlyAtSpecificDate' },
|
||||
],
|
||||
weekDays,
|
||||
weekDaysSelect: weekDays.map((val,ind) => { return { value: ind, text: val } }),
|
||||
configData: {
|
||||
"bottoken": "",
|
||||
"pager": {
|
||||
"url": "",
|
||||
},
|
||||
"menuSupport": false,
|
||||
"deliveryModes": []
|
||||
},
|
||||
presetSearchItems: [],
|
||||
}
|
||||
},
|
||||
created() {
|
||||
this.loadPresets()
|
||||
this.loadConfig()
|
||||
},
|
||||
methods: {
|
||||
loadPresets() {
|
||||
this.$http.get(window.location.pathname + 'api/deliveryPresets')
|
||||
.then(response => {
|
||||
this.presetSearchItems = response.body.map(x => { return {
|
||||
text: x.name,
|
||||
value: x.key,
|
||||
}})
|
||||
}, response => {
|
||||
})
|
||||
},
|
||||
loadConfig() {
|
||||
this.$http.get(window.location.pathname + 'config').then(response => {
|
||||
const newConfig = response.body
|
||||
newConfig.alarms = newConfig.alarms.map((x) => {
|
||||
x._id = btoa(JSON.stringify(x))
|
||||
return x
|
||||
})
|
||||
this.configData = newConfig
|
||||
}, response => {
|
||||
})
|
||||
},
|
||||
storeConfig() {
|
||||
const storeConfig = JSON.parse(JSON.stringify(this.configData))
|
||||
storeConfig.alarms = storeConfig.alarms.map(a=> {
|
||||
delete a._id
|
||||
return a
|
||||
})
|
||||
this.$http.post(window.location.pathname + 'config', storeConfig)
|
||||
.then(response => {})
|
||||
.then(this.$http.post(window.location.pathname + 'restart'))
|
||||
.then(() => {
|
||||
document.body.style = 'display:none'
|
||||
setTimeout(() => window.location.reload(), 1e3)
|
||||
})
|
||||
},
|
||||
addAlarm() {
|
||||
this.configData.alarms.push({
|
||||
name: "Target 1",
|
||||
preset: null,
|
||||
alarmSchedulingMode: "weekly",
|
||||
alarmTime: "13:37",
|
||||
callsign: "EDDM",
|
||||
weekDay: {
|
||||
"0": true,
|
||||
"1": true,
|
||||
"2": true,
|
||||
"3": true,
|
||||
"4": true,
|
||||
"5": true,
|
||||
"6": true
|
||||
},
|
||||
})
|
||||
},
|
||||
}
|
||||
})
|
||||
</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 it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
@ -0,0 +1,97 @@
|
||||
const config = require('./config.json')
|
||||
|
||||
const axios = require('axios')
|
||||
const fs = require('fs')
|
||||
const moment = require('moment')
|
||||
moment.locale('yes', {
|
||||
week : {
|
||||
dow : 1
|
||||
}
|
||||
})
|
||||
|
||||
|
||||
|
||||
const express = require('express')
|
||||
const appConfig = express()
|
||||
appConfig.use(express.json({ limit: '1mb' }))
|
||||
appConfig.use(express.static('html'))
|
||||
appConfig.use(express.static(__dirname + '/node_modules/@mdi/font'))
|
||||
|
||||
async function minuteCheck() {
|
||||
let timeRN = moment()
|
||||
//console.log('timeRN', timeRN.format("HH:mm"))
|
||||
for (let alarm of config.alarms) {
|
||||
let alarmTime = moment()
|
||||
.hours(+alarm.alarmTime.split(':') [ 0 ])
|
||||
.minutes(+alarm.alarmTime.split(':') [ 1 ])
|
||||
let secDiff = alarmTime.diff(timeRN, 'seconds')
|
||||
//
|
||||
//console.log(alarmTime.format("HH:mm"), secDiff)
|
||||
if (alarmTime.format("HH:mm") == timeRN.format("HH:mm")) {
|
||||
//console.log("TRIGGER", alarm.alarmSchedulingMode)
|
||||
let alarmTrigger = false
|
||||
switch (alarm.alarmSchedulingMode) {
|
||||
case 'monthlyAtSpecificDate':
|
||||
alarmTrigger = (timeRN.date() == alarmTime.specificWeekday)
|
||||
break;
|
||||
case 'monthlyFirstOccWeekday':
|
||||
// wish Date = alarmTime.firstOccWeekday
|
||||
let thisMonthFirstWeekDayOccurance = -1
|
||||
let monthFirstDay = moment().startOf('month')
|
||||
while (monthFirstDay.weekday() != alarm.firstOccWeekday) monthFirstDay.add(1, 'day')
|
||||
thisMonthFirstWeekDayOccurance = monthFirstDay.date()
|
||||
|
||||
//console.log(monthFirstDay.date(), monthFirstDay.weekday())
|
||||
//console.log('monthly first occurance for', alarm.firstOccWeekday, 'is', thisMonthFirstWeekDayOccurance)
|
||||
alarmTrigger = (timeRN.date() == thisMonthFirstWeekDayOccurance)
|
||||
break;
|
||||
case 'weekly':
|
||||
for (let weekDayKey of Object.keys(alarm.weekDay)) {
|
||||
//console.log(weekDayKey, alarm.weekDay)
|
||||
if (+weekDayKey === +timeRN.weekday()) {
|
||||
//console.log('weekday match', 'result is', alarm.weekDay[weekDayKey] )
|
||||
alarmTrigger = alarm.weekDay[weekDayKey]
|
||||
break;
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
if (alarmTrigger === true) {
|
||||
let eddmWeather = await axios.get(`https://aviationweather.gov/cgi-bin/data/metar.php?ids=${ alarm.callsign }&hours=0&order=id%2C-obs&sep=true`)
|
||||
console.log(eddmWeather.data)
|
||||
await axios.post(new URL(config.pager.url).origin + '/api/message/' + (!!alarm.preset ? 'preset' : 'advanced'), Object.assign( !!alarm.preset
|
||||
? { preset: alarm.preset }
|
||||
: { ...alarm.params } // backward compatibility
|
||||
, { payload: eddmWeather.data }))
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
async function main() {
|
||||
let now = moment()
|
||||
await minuteCheck()
|
||||
setTimeout(() => setInterval(minuteCheck, 60e3), now.diff(moment().add(1, 'minute'), 'seconds') * 1e3 + 5e3)
|
||||
}
|
||||
main()
|
||||
|
||||
/** 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.alarms)) return res.status(403).json(false)
|
||||
if (!(!!req.body.pager)) 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(3110, '0.0.0.0' || config.host || '127.0.0.1')
|
File diff suppressed because it is too large
Load Diff
@ -0,0 +1,8 @@
|
||||
{
|
||||
"dependencies": {
|
||||
"@mdi/font": "^7.0.96",
|
||||
"axios": "^1.2.0",
|
||||
"express": "^4.18.2",
|
||||
"moment": "^2.29.4"
|
||||
}
|
||||
}
|
Loading…
Reference in New Issue