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": { "emPuppettering": {
"enabled": true, "enabled": true,
"duplexTimeout": 360 "duplexTimeout": "250"
}, },
"emessage": { "emessage": {
"enabled": true, "enabled": true,
@ -50,6 +50,10 @@
"username": "test", "username": "test",
"password": "testsucks", "password": "testsucks",
"duplexTimeout": 300 "duplexTimeout": 300
},
"pnet": {
"enabled": true,
"duplexTimeout": 10
} }
}, },
"pagers": { "pagers": {

View file

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

View file

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

View file

@ -36,6 +36,9 @@ if (!!config.connectors.emPuppettering && config.connectors.emPuppettering.enabl
if (!!config.connectors.ecityruf && config.connectors.pagernetzAT.enabled === true) { if (!!config.connectors.ecityruf && config.connectors.pagernetzAT.enabled === true) {
types.ConnectorRegistry.register(new types.Connectors.PagernetzConnetorAT(connection)) 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()) 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.BirdySlim())
types.DeviceRegistry.register(new types.devices.EMessage2Ways()) types.DeviceRegistry.register(new types.devices.EMessage2Ways())
types.DeviceRegistry.register(new types.devices.Skyper()) 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 express = require('express')
const { MessageManager } = require('./types') const { MessageManager } = require('./types')

View file

@ -1,125 +1,125 @@
function Decoder(bytes, port) { function Decoder(bytes, port) {
var data = {} var data = {}
function bytesToString(a) { function bytesToString(a) {
return a.map(function(x){ return String.fromCharCode(x) }).join('') // because very OLD js parser return a.map(function(x){ return String.fromCharCode(x) }).join('') // because very OLD js parser
} }
switch (port) { switch (port) {
case 1: // Technical Received Acknowledgment '|5||C|' case 1: // Technical Received Acknowledgment '|5||C|'
// 31 32 33 34 35 20 // 31 32 33 34 35 20
data.type = 'ack' data.type = 'ack'
data.ack = 'recv' data.ack = 'recv'
data.msgid = bytesToString(bytes.splice(0,5)) data.msgid = bytesToString(bytes.splice(0,5))
data.rssi = bytes[0] data.rssi = -bytes[0] / 2
break; break;
case 2: // Read and Operational Acknowledgment 'AA|5|' and Operational '33|5|<CustomByte(s)>' case 2: // Read and Operational Acknowledgment 'AA|5|' and Operational '33|5|<CustomByte(s)>'
// AA 31 32 33 34 35 // AA 31 32 33 34 35
// operational ack with Payload "test"(maybe you should use a single byte) // operational ack with Payload "test"(maybe you should use a single byte)
// 33 31 32 33 34 35 54 65 73 74 // 33 31 32 33 34 35 54 65 73 74
data.type = 'ack' data.type = 'ack'
data.ack = bytes.length == 5 ? 'read' : 'operational' data.ack = bytes.length == 5 ? 'read' : 'operational'
data.msgid = bytesToString(bytes.splice(0,5)) data.msgid = bytesToString(bytes.splice(0,5))
if (data.ack === 'operational') { if (data.ack === 'operational') {
data.operationalData = bytes 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; if (bytes[0] == 0xFD) {
case 3: // Status & Canned Messages data.type = 'low_battery'
// 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; if (bytes[0] <= 0xAA) {
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 {
data.type = 'battery' data.type = 'battery'
data.battery = bytes[0] * 1 data.battery = bytes[0] * 1
data.isCharging = bytes[1] == 0xCC data.isCharging = false
} }
break; } else {
case 5: // OTAStatus + LoneWorker/SOS Trigger + GPS Tracking Port '|G|' data.type = 'battery'
// GPS Tracking '|G|' data.battery = bytes[0] * 1
//// 00 1E 33 D7 00 4A 2C 31 10 data.isCharging = bytes[1] == 0xCC
// SOS Start 'FF|G|' }
//// FF 00 1E 33 D7 00 4A 2C 31 10 break;
// SOS End 'FE|G|' case 5: // OTAStatus + LoneWorker/SOS Trigger + GPS Tracking Port '|G|'
//// FE 00 1E 33 D7 00 4A 2C 31 10 // GPS Tracking '|G|'
// LoneWorker Lack of movement 'FD|G|' //// 00 1E 33 D7 00 4A 2C 31 10
//// FD 00 1E 33 D7 00 4A 2C 31 10 // SOS Start 'FF|G|'
// LoneWorker Falldetect 'FC|G|' //// FF 00 1E 33 D7 00 4A 2C 31 10
//// FC 00 1E 33 D7 00 4A 2C 31 10 // SOS End 'FE|G|'
// LoneWorker End 'FB|G|' //// FE 00 1E 33 D7 00 4A 2C 31 10
//// FB 00 1E 33 D7 00 4A 2C 31 10 // LoneWorker Lack of movement 'FD|G|'
// OTA/PagerStatus 'FA|G||V|' //// FD 00 1E 33 D7 00 4A 2C 31 10
//// FA 00 1E 33 D7 00 4A 2C 31 10 28 // LoneWorker Falldetect 'FC|G|'
if (bytes.length !== 9) { // we have more than just the |G| block //// FC 00 1E 33 D7 00 4A 2C 31 10
if (bytes.length === 10) { // LoneWorker End 'FB|G|'
switch(bytes[0]) { //// FB 00 1E 33 D7 00 4A 2C 31 10
case 0xFF: // SOS Start // OTA/PagerStatus 'FA|G||V|'
data.type = 'sos' //// FA 00 1E 33 D7 00 4A 2C 31 10 28
data.sos = true if (bytes.length !== 9) { // we have more than just the |G| block
break; if (bytes.length === 10) {
case 0xFE: // SOS End switch(bytes[0]) {
data.type = 'sos' case 0xFF: // SOS Start
data.sos = false data.type = 'sos'
break; data.sos = true
case 0xFD: // LoneWorker Lack of movement break;
data.type = 'sos' case 0xFE: // SOS End
data.sos = false data.type = 'sos'
data.loneworker = 'lackofmovement' data.sos = false
break; break;
case 0xFC: // LoneWorker Falldetect case 0xFD: // LoneWorker Lack of movement
data.type = 'sos' data.type = 'sos'
data.sos = false data.sos = false
data.loneworker = 'falldetect' data.loneworker = 'lackofmovement'
break; break;
case 0xFB: // LoneWorker End case 0xFC: // LoneWorker Falldetect
data.type = 'sos' data.type = 'sos'
data.sos = false data.sos = false
data.loneworker = false data.loneworker = 'falldetect'
break; break;
} case 0xFB: // LoneWorker End
bytes.splice(0,1) data.type = 'sos'
data.sos = false
data.loneworker = false
break;
} }
if (bytes.length === 11) { // OTA PagerStatus bytes.splice(0,1)
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) if (bytes.length === 11) { // OTA PagerStatus
data.longitude = ((gpsBlock[0]<<24)>>>0) + ((gpsBlock[1]<<16)>>>0) + ((gpsBlock[2]<<8)>>>0) + gpsBlock[3] data.type = 'ota'
data.longitude /= 10e4 data.ota = 'status'
data.latitude = ((gpsBlock[4]<<24)>>>0) + ((gpsBlock[5]<<16)>>>0) + ((gpsBlock[6]<<8)>>>0) + gpsBlock[7] data.battery = bytes[10] * 1
data.latitude /= 10e4 bytes.splice(0,1)
data.lastGPSAcquisition = gpsBlock[8] }
break; } else { // we are having a normal gps block only
} data.type = 'gps'
return data; }
} 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, eMessagePuppeteerConnectorTwoWays,
PagernetzConnetorAT: require('./PagernetzConnetorAT'), PagernetzConnetorAT: require('./PagernetzConnetorAT'),
pNetConnector: require('./pNetConnector'),
LoRaWANConnector: require("./LoRaWANConnector"), LoRaWANConnector: require("./LoRaWANConnector"),
POCSAGConnector: require("./POCSAGConnector"), 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"), BirdySlim: require("./BirdySlim"),
EMessage2Ways: require("./EMessage2Ways"), EMessage2Ways: require("./EMessage2Ways"),
Skyper: require("./Skyper"), Skyper: require("./Skyper"),
TETRA_MXP600: require("./MXP600"),
TETRA_Generic: require("./GenericTetra"),
Device: require("./Device"), Device: require("./Device"),
} }