diff --git a/examples/bash/smartpager.sh b/examples/bash/smartpager.sh deleted file mode 100644 index 81fe023..0000000 --- a/examples/bash/smartpager.sh +++ /dev/null @@ -1,49 +0,0 @@ -#!/bin/bash -## Bash Library for sending out Pages and/or getting their State -# dependencies: curl, jq - -# echo "Bericht" | send_page "http://127.0.0.1:3000" "duplex" "birdyslim" "dapnet=dl-all#DXxxx" -send_page() { - text=$(cat -) - endpoint=$1 - msgType=$2 - deviceType=$3 - argArr=("$@") - connectorJSON=$(for arg in "${argArr[@]}"; do echo $arg; done | jq -Rcs '{array:split("\n")|.[3:-1]|map(split("=")|[(.[0]),.[1]]?)}.array') - echo $connectorJSON |\ - jq -c --arg t "$msgType" --arg d "$deviceType" --arg p "$text" '{type:$t, routing: {connectors:.,device:$d}, payload:$p}' |\ - curl -s -XPOST -H "Content-type: application/json" "$endpoint/api/message/advanced" -d @- |\ - jq -r . -} -msg_status() { - endpoint=$1 - msgid=$2 - curl -s "$endpoint/api/message/status/$msgid" |\ - jq -r ._routerData -} -is_msg_recv() { - msg_status $* | jq .recvAck | grep -q "true" - return $? -} -is_msg_read() { - msg_status $* | jq .readAck | grep -q "true" - return $? -} -is_msg_resp() { - msg_status $* | jq .response | grep -vq "false" - return $? -} - -ENDPOINT="http://127.0.0.1:3000" -#echo "Test" | send_page "$ENDPOINT" "duplex" "birdyslim" "pocsag=133701D" "test=123" -msgid=$(echo "Test" | send_page "$ENDPOINT" "duplex" "birdyslim" "pocsag=133701D" "test=123") -#msgid="Bznka" -msg_status "$ENDPOINT" "$msgid" - - -if is_msg_recv "$ENDPOINT" "$msgid"; then - echo "message is recv" -fi -if is_msg_read "$ENDPOINT" "$msgid"; then - echo "message is read" -fi diff --git a/index.js b/index.js index d781c3c..a097184 100644 --- a/index.js +++ b/index.js @@ -21,10 +21,14 @@ if (!!config.connectors.dapnet && config.connectors.dapnet.enabled === true) { if (!!config.connectors.ecityruf && config.connectors.ecityruf.enabled === true) { types.ConnectorRegistry.register(new types.Connectors.eCityrufConnector(connection)) } +if (!!config.connectors.emessage && config.connectors.emessage.enabled === true) { + types.ConnectorRegistry.register(new types.Connectors.eMessageConnector(connection)) +} types.ConnectorRegistry.register(new types.Connectors.DummyConnector()) 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()) const express = require('express') diff --git a/types/connectors/eMessageConnector.js b/types/connectors/eMessageConnector.js new file mode 100644 index 0000000..80b00d9 --- /dev/null +++ b/types/connectors/eMessageConnector.js @@ -0,0 +1,142 @@ +const Connector = require("./Connector") +const config = require('../../config.json') +const md5 = require('md5') +const axios = require('axios') +// [ "ecityruf", "123456789" ] +class eCityrufConnector extends Connector { + constructor (amqpConnMngr) { + super(amqpConnMngr) + this.name = "emessage" + this.duplexCapable = true + this.supportBOSkrypt = true + } + async transmitMessage(msg, params) { + const UUID = this.name+':'+md5(JSON.stringify([this.name,...params])) + const target = params[0] + if (target.split('#').length !== 2) throw 'No valid eMessage Parameter ' + const serviceName = target.split('#')[ 0 ], identifier = target.split('#')[ 1 ] + // eCityruf#8907737 + + let jwtToken = '', msgTrackID = '' + // login + axios.post('https://api.emessage.de/auth/login', { + username: config.connectors.emessage.username, + password: config.connectors.emessage.password + }) + .then((loginReq) => { // extract jwt + if (loginReq.data.apiStatusCode != 200) { throw loginReq } + jwtToken = loginReq.data.data.jwt + return jwtToken + }) + .then(() => { // send Message Request + return axios.post('https://api.emessage.de/rs/eSendMessages', { + messageText: msg.payload, + recipients: [{ serviceName, identifier }] + }, { + headers: { + 'Authorization': `Bearer ${ jwtToken }` + } + }) + }) + .then((sndMessageReq) => { // extract tracking ID from response + if (sndMessageReq.data.apiStatusCode != 200) { throw sndMessageReq } + this.connectorRegistry.reportState(msg, UUID, 'routed') // since we queued the message, we shall mark it was routed + msgTrackID = sndMessageReq.data.data.trackingId + console.log('TRACKING ID', msgTrackID) + return msgTrackID + }) + /*.then((_) => { // extract tracking ID from response + this.connectorRegistry.reportState(msg, UUID, 'routed') + msgTrackID = '04872f70-9029-11ec-b877-e5935093b691' + return msgTrackID + })*/ + .then(async () => { // check for response til duplexTimeout + const delaySeconds = (timeout) => new Promise(r=>setTimeout(r, timeout * 1e3)) + const checkDelivery = async () => { + try { + let res = await axios.get('https://api.emessage.de/rs/eGetMessages/External/' + msgTrackID, { + headers: { + 'Authorization': `Bearer ${ jwtToken }` + } + }) + let deliveryState = res.data.data.recipients[ 0 ].status[ 0 ].answer + if (deliveryState == 'INTRANSIT') this.connectorRegistry.reportState(msg, UUID, 'transit') + if (deliveryState == 'SENT') this.connectorRegistry.reportState(msg, UUID, 'sent') + if (deliveryState == 'RECEIVED' || +deliveryState > 0) { + this.connectorRegistry.reportDelivered(msg, UUID) + return true + } + console.log(JSON.stringify(res.data.data)) + //this.connectorRegistry.reportState(msg, UUID, deliveryState) + + return false + //if (res.data.data.recipients.filter(rec=>)) + } catch (e) { + throw e + } + } + const checkResponse = async () => { + try { + let res = await axios.get('https://api.emessage.de/rs/eGetMessages/External/' + msgTrackID, { + headers: { + 'Authorization': `Bearer ${ jwtToken }` + } + }) + let status = res.data.data.recipients[ 0 ].status[ 0 ] + //if (deliveryState == 'INTRANSIT') this.connectorRegistry.reportState(msg, UUID, 'transit') + /*if (deliveryState == 'RECEIVED' || +deliveryState > 0) { + this.connectorRegistry.reportDelivered(msg, UUID) + return true + }*/ + if (+status.answerNo == 1 && +status.answer > -1 ) { + //this.connectorRegistry.response(msg, status.answer) + require('../MessageManager').markMessageRead(msg.id) + require('../MessageManager').respondToMessage(msg.id, +status.answer) + return true + } + + return false + //if (res.data.data.recipients.filter(rec=>)) + } catch (e) { + throw e + } + } + + const deliveryProcess = new Promise(async (res, rej) => { + const { startInterval, duplexTimeout } = config.connectors.emessage + let nextDelay = startInterval + 0 + do { + let resultCheck = await checkDelivery() + if (resultCheck == true) return res() + await delaySeconds(nextDelay) + nextDelay *= 2 // wait 2x longer than before + } while (nextDelay <= duplexTimeout) + return rej() + }) + const responseProcess = new Promise(async (res, rej) => { + const { startInterval, responseTimeout } = config.connectors.emessage + let nextDelay = startInterval + 0 + do { + let resultCheck = await checkResponse() + if (resultCheck == true) return res() + await delaySeconds(nextDelay) + nextDelay *= 2 // wait 2x longer than before + } while (nextDelay <= responseTimeout) + return rej() + }) + return Promise.race([ + new Promise((res, rej) => { // exit pattern after duplexTimeout + setTimeout(rej, config.connectors.emessage.duplexTimeout * 1e3) + }), + deliveryProcess.then(responseProcess) + ]) + }) + .catch((err) => { + console.error(err) + this.connectorRegistry.reportFail(msg, UUID) + return false + }) + + } +} +module.exports = eCityrufConnector \ No newline at end of file diff --git a/types/connectors/index.js b/types/connectors/index.js index 7f01939..bf8b821 100644 --- a/types/connectors/index.js +++ b/types/connectors/index.js @@ -1,6 +1,7 @@ module.exports = { DAPNETConnector: require("./DAPNETConnector"), eCityrufConnector: require('./eCityrufConnector'), + eMessageConnector: require('./eMessageConnector'), LoRaWANConnector: require("./LoRaWANConnector"), POCSAGConnector: require("./POCSAGConnector"), DummyConnector: require("./DummyConnector"), diff --git a/types/devices/EMessage2Ways.js b/types/devices/EMessage2Ways.js new file mode 100644 index 0000000..feab770 --- /dev/null +++ b/types/devices/EMessage2Ways.js @@ -0,0 +1,92 @@ +const MessageManager = require("../MessageManager") +const PagerDevice = require("./Device") +const Str = require('@supercharge/strings') +const config = require('../../config.json') + +// Birdy Slim (IoT) Device +class EMessage2Ways extends PagerDevice { + constructor () { + super() + this.duplex = true + this.supportBOSkrypt = false + this.name = "emessage2WayS" + } + RandID() { + return `E${ Str.random(8) }` + } + async formatTX(msg) { + msg.id = this.RandID() + await MessageManager.BindMsg(msg) + //msg.payload = msg.type === 'duplex' ? `${ msg.id }${ msg.payload }` : msg.payload // only if duplex wanted we add the id + } + async tryReceive(data, connector) { + if (typeof(data) === 'object' && !!data.type) { + const stateSet = { + lastSeen: data.date + } + // If we have a Battery Measurement or other Power Events, we should store it + if (!!data.battery) stateSet.battery = data.battery/1e1 + if (data.hasOwnProperty('isCharging')) stateSet.isCharging = data.isCharging + if (data.hasOwnProperty('poweredOn')) stateSet.poweredOn = data.poweredOn + + // the same if we have an rssi measurement + if (!!data.rssi) stateSet.rssi = data.rssi + // and if we have the 3 components of a GPS Block + if (!!data.latitude && !!data.longitude && !!data.lastGPSAcquisition) stateSet.gps = { + lastGPSAcquisition: data.lastGPSAcquisition, + latitude: data.latitude, + longitude: data.longitude, + } + stateSet.lastLoRaPacket = data.metadata + console.log(data, stateSet) + /*if (!!data.metadata && !!data.metadata.uplink_message.rx_metadata) { + const rx_metadata = data.metadata.uplink_message.rx_metadata + + }*/ + + switch (data.type) { + case 'ack': { + switch (data.ack) { + case 'recv': + require('../ConnectorRegistry').reportDelivered({ id: data.msgid }, `lorawan:${ data.device_id }`) + break; + case 'read': + require('../MessageManager').markMessageRead(data.msgid) + break; + case 'operational': + require('../MessageManager').respondToMessage(data.msgid, data.operationalData) + break; + } + // If we have had a Ack. Event, we should store some Metadata about it too + require('../MessageManager').attachMetadata(data.msgid, { + ack: data.ack, + rssi: data.rssi, + date: data.date, + metadata: data.metadata, + }) + } + break; + case 'sos': + stateSet.sos = { + sos: data.sos, + date: data.date, + } + break; + + case 'status': + case 'cannedMessage': + case 'low_battery': + case 'power': + case 'battery': + case 'sos': + case 'gps': + require('../DeviceRegistry').deviceEvent(this.name, data.device_id, data) + break; + } + require('../DeviceRegistry').stateSet(this.name, data.device_id, stateSet) + return true + } + return false + } +} +module.exports = EMessage2Ways \ No newline at end of file diff --git a/types/devices/index.js b/types/devices/index.js index 18eb122..3fb93b4 100644 --- a/types/devices/index.js +++ b/types/devices/index.js @@ -1,6 +1,7 @@ module.exports = { GenericPager: require("./GenericPager"), BirdySlim: require("./BirdySlim"), + EMessage2Ways: require("./EMessage2Ways"), Skyper: require("./Skyper"), Device: require("./Device"), } \ No newline at end of file