UNPKG

sim800

Version:

A modern and opiniated module for SIM800 GSM modems ( SIM800 / SIM800L ).

299 lines 14.7 kB
"use strict"; var __importDefault = (this && this.__importDefault) || function (mod) { return (mod && mod.__esModule) ? mod : { "default": mod }; }; Object.defineProperty(exports, "__esModule", { value: true }); exports.Sim800Client = void 0; const rxjs_1 = require("rxjs"); const serialport_1 = require("serialport"); const stream_1 = __importDefault(require("stream")); const at_command_1 = require("./classes/at-command"); const cmgda_command_1 = require("./classes/cmgda-command"); const cmgf_command_1 = require("./classes/cmgf-command"); const cmgl_command_1 = require("./classes/cmgl-command"); const cnmi_command_1 = require("./classes/cnmi-command"); const cpin_status_command_1 = require("./classes/cpin-status-command"); const creg_status_command_1 = require("./classes/creg-status-command"); const pin_unlock_command_1 = require("./classes/pin-unlock-command"); const sim800_sms_1 = require("./classes/sim800-sms"); const sim800_data_handler_1 = require("./handlers/sim800.data.handler"); const sim800_error_handler_1 = require("./handlers/sim800.error.handler"); const sim800_open_handler_1 = require("./handlers/sim800.open.handler"); const attach_serial_listeners_1 = require("./helpers/attach-serial-listeners"); const sim800_client_state_enum_1 = require("./interfaces/sim800-client-state.enum"); const sim800_command_enums_1 = require("./interfaces/sim800-command.enums"); const sim800_pin_state_enum_1 = require("./interfaces/sim800-pin-state.enum"); const completed_command_subscriber_1 = require("./subscribers/completed-command-subscriber"); const delivery_report_input_subscriber_1 = require("./subscribers/delivery-report-input-subscriber"); const delivery_report_stream_subscriber_1 = require("./subscribers/delivery-report-stream-subscriber"); const new_sms_subscriber_1 = require("./subscribers/new-sms-subscriber"); const sms_stream_subscriber_1 = require("./subscribers/sms-stream-subscriber"); const interfaces_1 = require("./interfaces"); class Sim800Client { constructor({ port, baudRate, delimiter, logger, pin, preventWipe, noInit }) { this.eventEmitter = new stream_1.default(); this.nextJob$ = new rxjs_1.Subject(); this.buffer = []; this.cnmi = '2,1,2,1,0'; this.ready$ = new rxjs_1.AsyncSubject(); this.network$ = new rxjs_1.AsyncSubject(); this.smsBusy$ = new rxjs_1.AsyncSubject(); this.stream$ = new rxjs_1.Subject(); this.inputStream$ = new rxjs_1.Subject(); this.smsStream$ = new rxjs_1.Subject(); this.deliveryReportStream$ = new rxjs_1.Subject(); this.smsQueuePing$ = new rxjs_1.Subject(); this.state = sim800_client_state_enum_1.Sim800ClientState.Idle; this.incomingSms = []; this.outboxSpooler = []; this.receivingDeliveryReport = false; this.smsQueue = []; this.handleInputData = (data) => { const bufferCopy = [...this.buffer]; // Copying buffer to prevent side effects of a command being removed from the buffer while the function is running // Is data part of command? if (!((bufferCopy.length && bufferCopy[0].isDataPartOfRunningCommand(data)) || data === 'OK' || data === 'ERROR')) { this.eventEmitter.emit('input', data); this.inputStream$.next(data); } }; this.port = port; this.preventWipe = preventWipe || false; this.pin = pin; this.noInit = noInit || false; this.baudRate = baudRate || 115200; this.delimiter = delimiter || '\r\n'; this.logger = logger || console; this.parser = new serialport_1.ReadlineParser({ delimiter: this.delimiter, }); this.serial = new serialport_1.SerialPort({ path: this.port, baudRate: this.baudRate }); this.serial.pipe(this.parser); (0, attach_serial_listeners_1.attachSerialListeners)(this.serial, this.parser, { open: () => { this.eventEmitter.emit('deviceReady'); (0, sim800_open_handler_1.sim800OpenHandler)(this.ready$); }, data: (data) => (0, sim800_data_handler_1.sim800DataHandler)(data, this.stream$), error: (error) => (0, sim800_error_handler_1.sim800ErrorHandler)(error, logger), }); this.nextJob$.subscribe(async () => { await this.awaitDevice(); const command = this.buffer[0]; command.send(this.stream$, this.serial); }); this.stream$.subscribe(this.handleInputData); this.inputStream$.subscribe((0, new_sms_subscriber_1.newSmsSubscriberFactory)(this, this.logger)); this.inputStream$.subscribe((0, delivery_report_input_subscriber_1.deliveryReportInputSubscriberFactory)(this, this.logger)); this.deliveryReportStream$.subscribe((0, delivery_report_stream_subscriber_1.deliveryReportStreamSubscriberFactory)(this, this.logger)); this.smsStream$.subscribe((0, sms_stream_subscriber_1.smsStreamSubscriberFactory)(this)); this.smsQueuePing$.subscribe(() => { var _a, _b, _c, _d, _e, _f; (_b = (_a = this.logger) === null || _a === void 0 ? void 0 : _a.verbose) === null || _b === void 0 ? void 0 : _b.call(_a, 'an sms job has ended, shifting queue...'); this.smsQueue.shift(); if (this.smsQueue.length) { (_d = (_c = this.logger) === null || _c === void 0 ? void 0 : _c.debug) === null || _d === void 0 ? void 0 : _d.call(_c, 'sending next SMS in line...'); this.smsQueue[0].execute(); } else { (_f = (_e = this.logger) === null || _e === void 0 ? void 0 : _e.debug) === null || _f === void 0 ? void 0 : _f.call(_e, 'no more SMS in queue, waiting...'); } }); if (!this.noInit) { this.init(); } } async init() { var _a, _b; try { await (0, rxjs_1.lastValueFrom)((0, rxjs_1.from)(this.awaitDevice()).pipe((0, rxjs_1.timeout)(5000))); // Checking if device is an AT modem await this.send(new at_command_1.AtCommand()); // Handling Pin Status try { await this.handlePinState((await this.send(new cpin_status_command_1.CpinStatusCommand()))); } catch (err) { this.eventEmitter.emit('error', new Error('Error while handling PIN code')); throw new Error(`Error while handling PIN code, please check that the pin is correct before trying again to prevent sim card lock`); } this.state = sim800_client_state_enum_1.Sim800ClientState.Initialized; (_a = this.logger) === null || _a === void 0 ? void 0 : _a.log('Sim800Client Initialized, waiting for network'); this.monitorNetworkUntilReady(this.network$); // Change SIM Mode await this.send(new cnmi_command_1.CnmiCommand(this.cnmi)); // Change to PDU await this.send(new cmgf_command_1.CmgfCommand(sim800_command_enums_1.CmgfMode.Pdu)); if (!this.preventWipe) { this.deleteAllStoredSms(); } } catch (error) { if (typeof error === 'object' && error && 'message' in error) { this.eventEmitter.emit('error', error); (_b = this.logger) === null || _b === void 0 ? void 0 : _b.error(`Sim800Client init error : ${error.message}`); } } } awaitDevice() { return (0, rxjs_1.lastValueFrom)(this.ready$); } isNetworkReady() { return (0, rxjs_1.lastValueFrom)(this.network$); } async send(command, options) { var _a, _b; // we subscribe to the completed observable const subscription = command.completed$.subscribe((0, completed_command_subscriber_1.completedCommandSubscriberFactory)(command, this.buffer, this.nextJob$, this.logger)); // we add the command to the buffer this.buffer.push(command); // if the buffer has only one command, we execute it if (this.buffer.length === 1) { (_b = (_a = this.logger) === null || _a === void 0 ? void 0 : _a.verbose) === null || _b === void 0 ? void 0 : _b.call(_a, `Executing "${command.command}" with PID ${command.pid}`); this.nextJob$.next(); } await (0, rxjs_1.lastValueFrom)(command.completed$); subscription.unsubscribe(); if (command.error) { this.eventEmitter.emit('error', command.error); throw command.error; } if (options === null || options === void 0 ? void 0 : options.raw) { return command.raw; } return command.result; } async sendSms(number, text, deliveryReport = false) { var _a, _b, _c, _d, _e, _f, _g; (_b = (_a = this.logger) === null || _a === void 0 ? void 0 : _a.debug) === null || _b === void 0 ? void 0 : _b.call(_a, `sendSms called for number: "${number}", waiting line free and network ready`); const networkReady = await this.isNetworkReady(); if (!networkReady) { throw new Error('network stalled, please reset the device and try again'); } const sms = new sim800_sms_1.Sim800Sms(this, { number, text, deliveryReport }); this.smsQueue.push(sms); if (this.smsQueue.length === 1) { // if the queue is now of length 1, it means that the sms is the only one in the queue // we can send it right away (_d = (_c = this.logger) === null || _c === void 0 ? void 0 : _c.verbose) === null || _d === void 0 ? void 0 : _d.call(_c, `sms queue was empty, sending sms right away`); sms.execute(); } else { (_f = (_e = this.logger) === null || _e === void 0 ? void 0 : _e.verbose) === null || _f === void 0 ? void 0 : _f.call(_e, `sms is #${this.smsQueue.length} in queue, waiting for the line to be free`); } try { const compositeId = await (0, rxjs_1.lastValueFrom)(sms.result$); this.eventEmitter.emit('sms-sent', compositeId, new Date()); return compositeId; } catch (error) { this.eventEmitter.emit('error', error); (_g = this.logger) === null || _g === void 0 ? void 0 : _g.error('ERROR', error); } } async handlePinState(status) { var _a; switch (status) { case sim800_pin_state_enum_1.Sim800PinState.PinRequired: if (this.pin) { (_a = this.logger) === null || _a === void 0 ? void 0 : _a.warn('Pin required, unlocking sim card with provided pin'); return this.send(new pin_unlock_command_1.PinUnlockCommand(this.pin)); } else { throw new Error('Pin required but not provided'); } case sim800_pin_state_enum_1.Sim800PinState.PukRequired: throw new Error('Sim locked, puk required, please use a mobile phone to unlock the sim card'); case sim800_pin_state_enum_1.Sim800PinState.Ready: break; } } /** * * @deprecated will be removed from the next minor release */ checkNetwork(network$) { this.monitorNetworkUntilReady(network$); } monitorNetworkUntilReady(network$) { (0, rxjs_1.interval)(5000) .pipe((0, rxjs_1.takeUntil)(network$)) .subscribe(async () => { const networkResult = (await this.send(new creg_status_command_1.CregStatusCommand())); this.handleNetworkResult(networkResult); }); } async getNetworkStatus() { const result = (await this.send(new creg_status_command_1.CregStatusCommand())); return result.slice(-1) || interfaces_1.NetworkStatus.Unknown; } setNetworkReady() { this.eventEmitter.emit('networkReady'); this.network$.next(true); this.network$.complete(); } setNetworkNotReady() { this.network$.next(false); this.network$.complete(); this.network$.unsubscribe(); this.network$ = new rxjs_1.AsyncSubject(); // check CREG or maybe COPS, reset and call back init if not pin error } handleNetworkResult(result) { var _a; switch (result.slice(-1)) { case '1': this.setNetworkReady(); break; case '5': this.setNetworkReady(); break; case '3': (_a = this.logger) === null || _a === void 0 ? void 0 : _a.warn('Network registration denied'); this.setNetworkNotReady(); break; } } reset(emptyBuffers = false, gracePeriodMs = 10000) { var _a, _b; if (emptyBuffers) { this.buffer = []; this.incomingSms = []; this.outboxSpooler = []; this.smsQueue = []; } (_a = this.logger) === null || _a === void 0 ? void 0 : _a.warn('Sim800Client reset called'); this.setNetworkNotReady(); this.serial.write(String.fromCharCode(27)); this.serial.write('AT+CFUN=1,1\r\n'); (_b = this.logger) === null || _b === void 0 ? void 0 : _b.warn(`Sim800Client reset done, waiting ${gracePeriodMs}ms before reinitializing`); if (!this.noInit) { setTimeout(() => { this.init(); }, gracePeriodMs); } } async deleteAllStoredSms() { // By default, wipe every SMS if there is any, we must wait for a network ready indication // and wait for another 2-3 seconds for the SIM to be ready await this.isNetworkReady(); setTimeout(async () => { var _a; try { const result = await this.send(new cmgl_command_1.CmglCommand(), { raw: true }); if (result === null || result === void 0 ? void 0 : result.length) { await this.send(new cmgda_command_1.CmgdaCommand()); } } catch (error) { (_a = this.logger) === null || _a === void 0 ? void 0 : _a.warn("Couldn't perform the CMGL command properly, maybe SIM not yet fully initialized"); this.eventEmitter.emit('error', error); } }, 2000); } on(event, listener) { return this.eventEmitter.on(event, listener); } } exports.Sim800Client = Sim800Client; //# sourceMappingURL=sim800.client.js.map