modem.js 4.92 KB
'use strict';

const INTERVAL_BEETWEN_SIGNAL_STRENGTH_MS = 60000;
const DELIMITER_WAIT_FOR_OK = '\nOK\r\n';

const SerialPort = require('serialport');
const ParserReadline = require('@serialport/parser-readline');
const ParserDelimiter = require('@serialport/parser-delimiter');

const config = require('komodo-sdk/config');
const logger = require('komodo-sdk/logger');

const mutex = require('./mutex');
const common = require('./common');
const sms = require('./sms');

let imsi;
let signalStrength;

const port = new SerialPort(config.modem.device, { baudRate: 115200 });

const parserReadLine = new ParserReadline();
const parserWaitForOK = new ParserDelimiter({ delimiter: DELIMITER_WAIT_FOR_OK });
port.pipe(parserReadLine);

function writeToPort(data) {
    return new Promise((resolve) => {
        port.write(data, (err, bytesWritten) => {
            if (err) logger.warn(`ERROR: ${err.toString()}`);
            logger.verbose(`* OUT: ${data}`);
            resolve(bytesWritten);
        });
    });
}

async function writeToPortAndWaitForOK(data) {
    await mutex.setLockWaitForOK();
    const result = await writeToPort(data);
    await mutex.setLockWaitForOK();
    mutex.releaseLockWaitForOK();
    return result;
}

async function readSMS(slot) {
    const parser = new ParserDelimiter({ delimiter: DELIMITER_WAIT_FOR_OK });
    parser.on('data', async (data) => {
        if (data) {
            const smsObject = sms.extract(data.toString().trim());
            console.log('SMS', smsObject); // eslint-disable-line no-console
        }
        mutex.releaseLockWaitForOK();
    });

    logger.info(`Reading SMS on slot ${slot}`);
    port.pipe(parser);
    await writeToPortAndWaitForOK(`AT+CMGR=${slot}\r`);
    port.unpipe(parser);
    logger.verbose(`Finished reading SMS on slot ${slot}`);

    logger.info(`Deleting message on slot ${slot}`);
    port.pipe(parserWaitForOK);
    await writeToPortAndWaitForOK(`AT+CMGD=${slot}\r`);
    port.unpipe(parserWaitForOK);

    logger.info('Message processing has completed');
}

function onIncomingSMS(data) {
    const value = common.extractValueFromReadLineData(data);
    if (!value) return;

    const chunks = value.split(',');
    if (!chunks && !chunks[1]) return;

    const slot = chunks[1];

    logger.info(`Incoming SMS on slot ${slot}`);
    readSMS(slot);
}

parserReadLine.on('data', (data) => {
    logger.verbose(`* IN: ${data}`);
    if (data) {
        if (data.indexOf('+CSQ: ') === 0) {
            signalStrength = common.extractValueFromReadLineData(data);
            logger.info(`Signal strength: ${signalStrength}`);
        } else if (data.indexOf('+CMTI: ') === 0) {
            onIncomingSMS(data);
        }
    }
});

parserWaitForOK.on('data', () => {
    mutex.releaseLockWaitForOK();
});

async function readIMSI() {
    logger.info('Querying IMSI');
    const parserReadIMSI = new ParserDelimiter({ delimiter: DELIMITER_WAIT_FOR_OK });
    parserReadIMSI.on('data', (data) => {
        if (data) {
            imsi = data.toString().trim();
            logger.info(`IMSI: ${imsi}`);
        }
        mutex.releaseLockWaitForOK();
    });

    port.pipe(parserReadIMSI);
    await writeToPortAndWaitForOK('AT+CIMI\r');
    await mutex.setLockWaitForOK();
    mutex.releaseLockWaitForOK();
    port.unpipe(parserReadIMSI);
}

async function querySignalStrength() {
    port.pipe(parserWaitForOK);
    await writeToPortAndWaitForOK('AT+CSQ\r');
    port.unpipe(parserWaitForOK);
}

async function registerSignalStrengthBackgroundQuery() {
    logger.info('Registering background signal strength query');

    setInterval(() => {
        querySignalStrength();
    }, INTERVAL_BEETWEN_SIGNAL_STRENGTH_MS);
}

async function sendSMS(destination, msg) {
    await mutex.setLockWaitForCommand();
    logger.info('Sending message', { destination, msg });

    const correctedDestination = `+${destination}`.replace(/^0/, '62').replace(/^\++/, '+');

    port.pipe(parserWaitForOK);
    await writeToPortAndWaitForOK('AT+CMGF=1\r');
    await writeToPortAndWaitForOK(`AT+CMGS="${correctedDestination}"\n${msg}${Buffer.from([0x1A])}`);
    port.unpipe(parserWaitForOK);

    logger.info('Message has been sent');

    mutex.releaseLockWaitForCommand();
}

function init() {
    port.on('open', async () => {
        port.pipe(parserWaitForOK);

        logger.info('Modem opened');
        await writeToPortAndWaitForOK('AT\r');

        logger.info('Initializing modem to factory set');
        await writeToPortAndWaitForOK('AT&F\r');

        logger.info('Disabling echo');
        await writeToPortAndWaitForOK('ATE0\r');

        logger.info('Querying signal strength');
        await writeToPortAndWaitForOK('AT+CSQ\r');

        logger.info('Deleting existing messages');
        // await writeToPortAndWaitForOK('AT+CMGD=0,4\r');

        await readIMSI();

        port.unpipe(parserWaitForOK);

        registerSignalStrengthBackgroundQuery();
        logger.verbose('Init completed');
    });
}

init();

exports.sendSMS = sendSMS;