before update

This commit is contained in:
cheetah 2025-03-23 22:37:20 +01:00
parent 2814584831
commit 7eb1aa78ab
10 changed files with 239 additions and 118 deletions

View file

@ -26,7 +26,7 @@
},
"emPuppettering": {
"enabled": true,
"duplexTimeout": 360
"duplexTimeout": "250"
},
"emessage": {
"enabled": true,
@ -50,6 +50,10 @@
"username": "test",
"password": "testsucks",
"duplexTimeout": 300
},
"pnet": {
"enabled": true,
"duplexTimeout": 10
}
},
"pagers": {

View file

@ -325,6 +325,8 @@
deviceType: [
{ k: 'Generic', v: 'generic' },
{ k: 'Birdy Slim (IoT)', v: 'birdyslim' },
{ k: 'MXP600', v: 'mxp600' },
{ k: 'Generic Tetra', v: 'tetra-generic' },
],
connectorTypes: [
{ k: 'Dummy', v: 'dummy' },
@ -339,6 +341,8 @@
{ k: 'e*2wayS alarmManager', v: 'em-a-twoways' },
{ k: 'LoRaWAN TTNv3', v: 'lorawan' },
{ k: 'pNET TETRA', v: 'pnet' },
],
configData: {
boskrypt: {

View file

@ -217,6 +217,8 @@ Last LoRaWAN Packet:
deviceType: [
{k: 'Generic', v: 'generic'},
{k: 'Birdy Slim (IoT)', v: 'birdyslim'},
{ k: 'MXP600', v: 'mxp600' },
{ k: 'Generic Tetra', v: 'tetra-generic' },
],
connectorTypes: [
{k: 'Dummy', v: 'dummy'},
@ -231,6 +233,8 @@ Last LoRaWAN Packet:
{k: 'e*2wayS alarmManager', v: 'em-a-twoways'},
{k: 'LoRaWAN TTNv3', v: 'lorawan'},
{ k: 'pNET TETRA', v: 'pnet' },
],
newMSGData: {

View file

@ -36,6 +36,9 @@ if (!!config.connectors.emPuppettering && config.connectors.emPuppettering.enabl
if (!!config.connectors.ecityruf && config.connectors.pagernetzAT.enabled === true) {
types.ConnectorRegistry.register(new types.Connectors.PagernetzConnetorAT(connection))
}
if (!!config.connectors.ecityruf && config.connectors.pnet.enabled === true) {
types.ConnectorRegistry.register(new types.Connectors.pNetConnector())
}
types.ConnectorRegistry.register(new types.Connectors.DummyConnector())
@ -43,6 +46,9 @@ types.DeviceRegistry.register(new types.devices.GenericPager())
types.DeviceRegistry.register(new types.devices.BirdySlim())
types.DeviceRegistry.register(new types.devices.EMessage2Ways())
types.DeviceRegistry.register(new types.devices.Skyper())
//new tetra devices
types.DeviceRegistry.register(new types.devices.TETRA_Generic())
types.DeviceRegistry.register(new types.devices.TETRA_MXP600())
const express = require('express')
const { MessageManager } = require('./types')

View file

@ -1,125 +1,125 @@
function Decoder(bytes, port) {
var data = {}
function bytesToString(a) {
return a.map(function(x){ return String.fromCharCode(x) }).join('') // because very OLD js parser
}
switch (port) {
case 1: // Technical Received Acknowledgment '|5||C|'
// 31 32 33 34 35 20
data.type = 'ack'
data.ack = 'recv'
data.msgid = bytesToString(bytes.splice(0,5))
data.rssi = bytes[0]
break;
case 2: // Read and Operational Acknowledgment 'AA|5|' and Operational '33|5|<CustomByte(s)>'
// AA 31 32 33 34 35
// operational ack with Payload "test"(maybe you should use a single byte)
// 33 31 32 33 34 35 54 65 73 74
data.type = 'ack'
data.ack = bytes.length == 5 ? 'read' : 'operational'
data.msgid = bytesToString(bytes.splice(0,5))
if (data.ack === 'operational') {
data.operationalData = bytes
var data = {}
function bytesToString(a) {
return a.map(function(x){ return String.fromCharCode(x) }).join('') // because very OLD js parser
}
switch (port) {
case 1: // Technical Received Acknowledgment '|5||C|'
// 31 32 33 34 35 20
data.type = 'ack'
data.ack = 'recv'
data.msgid = bytesToString(bytes.splice(0,5))
data.rssi = -bytes[0] / 2
break;
case 2: // Read and Operational Acknowledgment 'AA|5|' and Operational '33|5|<CustomByte(s)>'
// AA 31 32 33 34 35
// operational ack with Payload "test"(maybe you should use a single byte)
// 33 31 32 33 34 35 54 65 73 74
data.type = 'ack'
data.ack = bytes.length == 5 ? 'read' : 'operational'
data.msgid = bytesToString(bytes.splice(0,5))
if (data.ack === 'operational') {
data.operationalData = bytes
}
break;
case 3: // Status & Canned Messages
// Canned Message '01' --- '05', single Hex Number, incrementing
// Status '<StatusIncrID>|Z|' or if no CenterSelection '<StatusIncrID>00'
if (bytes.length == 1) {
data.type = 'cannedMessage'
data.cannedMessage = bytes[0] * 1
} else {
data.type = 'status'
data.status = bytes[0] * 1
data.selection = bytes[1] * 1
}
break;
case 4: // Battery and Power Notifications
// Startup 'FF'
// Shutdown 'EE'
// Low Battery 'FD'
// ChargingOn '|V|CC'
// ChargingOff'|V|BB'
if (bytes.length == 1) {
data.type = 'power'
if (bytes[0] >= 0xEE) {
data.poweredOn = bytes[0]== 0xFF
}
break;
case 3: // Status & Canned Messages
// Canned Message '01' --- '05', single Hex Number, incrementing
// Status '<StatusIncrID>|Z|' or if no CenterSelection '<StatusIncrID>00'
if (bytes.length == 1) {
data.type = 'cannedMessage'
data.cannedMessage = bytes[0] * 1
} else {
data.type = 'status'
data.status = bytes[0] * 1
data.selection = bytes[1] * 1
if (bytes[0] == 0xFD) {
data.type = 'low_battery'
}
break;
case 4: // Battery and Power Notifications
// Startup 'FF'
// Shutdown 'EE'
// Low Battery 'FD'
// ChargingOn '|V|CC'
// ChargingOff'|V|BB'
if (bytes.length == 1) {
data.type = 'power'
if (bytes[0] >= 0xEE) {
data.poweredOn = bytes[0]== 0xFF
}
if (bytes[0] == 0xFD) {
data.type = 'low_battery'
}
if (bytes[0] <= 0xAA) {
data.type = 'battery'
data.battery = bytes[0] * 1
data.isCharging = false
}
} else {
if (bytes[0] <= 0xAA) {
data.type = 'battery'
data.battery = bytes[0] * 1
data.isCharging = bytes[1] == 0xCC
data.isCharging = false
}
break;
case 5: // OTAStatus + LoneWorker/SOS Trigger + GPS Tracking Port '|G|'
// GPS Tracking '|G|'
//// 00 1E 33 D7 00 4A 2C 31 10
// SOS Start 'FF|G|'
//// FF 00 1E 33 D7 00 4A 2C 31 10
// SOS End 'FE|G|'
//// FE 00 1E 33 D7 00 4A 2C 31 10
// LoneWorker Lack of movement 'FD|G|'
//// FD 00 1E 33 D7 00 4A 2C 31 10
// LoneWorker Falldetect 'FC|G|'
//// FC 00 1E 33 D7 00 4A 2C 31 10
// LoneWorker End 'FB|G|'
//// FB 00 1E 33 D7 00 4A 2C 31 10
// OTA/PagerStatus 'FA|G||V|'
//// FA 00 1E 33 D7 00 4A 2C 31 10 28
if (bytes.length !== 9) { // we have more than just the |G| block
if (bytes.length === 10) {
switch(bytes[0]) {
case 0xFF: // SOS Start
data.type = 'sos'
data.sos = true
break;
case 0xFE: // SOS End
data.type = 'sos'
data.sos = false
break;
case 0xFD: // LoneWorker Lack of movement
data.type = 'sos'
data.sos = false
data.loneworker = 'lackofmovement'
break;
case 0xFC: // LoneWorker Falldetect
data.type = 'sos'
data.sos = false
data.loneworker = 'falldetect'
break;
case 0xFB: // LoneWorker End
data.type = 'sos'
data.sos = false
data.loneworker = false
break;
}
bytes.splice(0,1)
} else {
data.type = 'battery'
data.battery = bytes[0] * 1
data.isCharging = bytes[1] == 0xCC
}
break;
case 5: // OTAStatus + LoneWorker/SOS Trigger + GPS Tracking Port '|G|'
// GPS Tracking '|G|'
//// 00 1E 33 D7 00 4A 2C 31 10
// SOS Start 'FF|G|'
//// FF 00 1E 33 D7 00 4A 2C 31 10
// SOS End 'FE|G|'
//// FE 00 1E 33 D7 00 4A 2C 31 10
// LoneWorker Lack of movement 'FD|G|'
//// FD 00 1E 33 D7 00 4A 2C 31 10
// LoneWorker Falldetect 'FC|G|'
//// FC 00 1E 33 D7 00 4A 2C 31 10
// LoneWorker End 'FB|G|'
//// FB 00 1E 33 D7 00 4A 2C 31 10
// OTA/PagerStatus 'FA|G||V|'
//// FA 00 1E 33 D7 00 4A 2C 31 10 28
if (bytes.length !== 9) { // we have more than just the |G| block
if (bytes.length === 10) {
switch(bytes[0]) {
case 0xFF: // SOS Start
data.type = 'sos'
data.sos = true
break;
case 0xFE: // SOS End
data.type = 'sos'
data.sos = false
break;
case 0xFD: // LoneWorker Lack of movement
data.type = 'sos'
data.sos = false
data.loneworker = 'lackofmovement'
break;
case 0xFC: // LoneWorker Falldetect
data.type = 'sos'
data.sos = false
data.loneworker = 'falldetect'
break;
case 0xFB: // LoneWorker End
data.type = 'sos'
data.sos = false
data.loneworker = false
break;
}
if (bytes.length === 11) { // OTA PagerStatus
data.type = 'ota'
data.ota = 'status'
data.battery = bytes[10] * 1
bytes.splice(0,1)
}
} else { // we are having a normal gps block only
data.type = 'gps'
bytes.splice(0,1)
}
var gpsBlock = bytes.splice(0,9)
data.longitude = ((gpsBlock[0]<<24)>>>0) + ((gpsBlock[1]<<16)>>>0) + ((gpsBlock[2]<<8)>>>0) + gpsBlock[3]
data.longitude /= 10e4
data.latitude = ((gpsBlock[4]<<24)>>>0) + ((gpsBlock[5]<<16)>>>0) + ((gpsBlock[6]<<8)>>>0) + gpsBlock[7]
data.latitude /= 10e4
data.lastGPSAcquisition = gpsBlock[8]
break;
}
return data;
}
if (bytes.length === 11) { // OTA PagerStatus
data.type = 'ota'
data.ota = 'status'
data.battery = bytes[10] * 1
bytes.splice(0,1)
}
} else { // we are having a normal gps block only
data.type = 'gps'
}
var gpsBlock = bytes.splice(0,9)
data.longitude = ((gpsBlock[0]<<24)>>>0) + ((gpsBlock[1]<<16)>>>0) + ((gpsBlock[2]<<8)>>>0) + gpsBlock[3]
data.longitude /= 10e4
data.latitude = ((gpsBlock[4]<<24)>>>0) + ((gpsBlock[5]<<16)>>>0) + ((gpsBlock[6]<<8)>>>0) + gpsBlock[7]
data.latitude /= 10e4
data.lastGPSAcquisition = gpsBlock[8]
break;
}
return data;
}

View file

@ -22,6 +22,7 @@ module.exports = {
eMessagePuppeteerConnectorTwoWays,
PagernetzConnetorAT: require('./PagernetzConnetorAT'),
pNetConnector: require('./pNetConnector'),
LoRaWANConnector: require("./LoRaWANConnector"),
POCSAGConnector: require("./POCSAGConnector"),

View file

@ -0,0 +1,59 @@
const Connector = require("./Connector")
const config = require('../../config.json')
const md5 = require('md5')
const axios = require('axios')
const { io } = require("socket.io-client")
class pNetConnector extends Connector {
constructor (amqpConnMngr) {
super(amqpConnMngr)
this.name = "pnet"
this.duplexCapable = true
this.managementSocket = false
}
async transmitMessage(msg, params) {
console.log('pnet socket=',this.managementSocket)
if (!this.managementSocket) {
console.log('starting pnet socket')
this.managementSocket = io("wss://<pnet url>")
this.managementSocket.on('smartpager:ingress:event', (evt) => {
console.log(evt)
//this.connectorRegistry.reportState(msg, evt.UUID, evt.state)
if (evt.delivered) this.connectorRegistry.reportDelivered({ id: evt.msgid }, `tetra:${ evt.device_id }`)
//require('../MessageManager').markMessageRead(data.msgid)
if (evt.response) require('../MessageManager').respondToMessage(evt.msgid, evt.response)
})
await this.managementSocket.connect()
}
const UUID = this.name+':'+md5(JSON.stringify([this.name,...params]))
if (params.length < 1) return false
const target = params[0]
if (target.split('#').length !== 2) throw 'No valid pNet Parameter <transmitterGroup#callSign>'
const contact = target.split('#')[ 0 ], options = target.split('#')[ 1 ]
/*const extraParameters = {
auth: {
username: config.connectors.dapnet.username,
password: config.connectors.dapnet.password
}
}*/
this.managementSocket.emit('smartpager:ingress', {
UUID,
id: msg.id,
text: msg.payload,
contact,
options,
})
return true
/*return axios.post("https://pnet.cheetah.cat/", dapnetRequest, extraParameters)
.then(() => {
return true
})
.catch((err) => {
console.error(err.response.data)
this.connectorRegistry.reportFail(msg, UUID)
return false
})*/
}
}
module.exports = pNetConnector

View file

@ -0,0 +1,20 @@
const MessageManager = require("../MessageManager")
const PagerDevice = require("./Device")
const Str = require('@supercharge/strings')
class GenericTetra extends PagerDevice {
constructor () {
super()
this.duplex = true
this.name = "tetra-generic"
}
RandID() {
return `TETRA${ Str.random(8) }`
}
async formatTX(msg) {
msg.id = this.RandID()
await MessageManager.BindMsg(msg)
// return msg
}
}
module.exports = GenericTetra

20
types/devices/MXP600.js Normal file
View file

@ -0,0 +1,20 @@
const MessageManager = require("../MessageManager")
const PagerDevice = require("./Device")
const Str = require('@supercharge/strings')
class TETRA_MXP600 extends PagerDevice {
constructor () {
super()
this.duplex = true
this.name = "mxp600"
}
RandID() {
return `MXP${ Str.random(8) }`
}
async formatTX(msg) {
msg.id = this.RandID()
await MessageManager.BindMsg(msg)
// return msg
}
}
module.exports = TETRA_MXP600

View file

@ -3,5 +3,8 @@ module.exports = {
BirdySlim: require("./BirdySlim"),
EMessage2Ways: require("./EMessage2Ways"),
Skyper: require("./Skyper"),
TETRA_MXP600: require("./MXP600"),
TETRA_Generic: require("./GenericTetra"),
Device: require("./Device"),
}