import {NFC} from "nfc-pcsc"; import {IPCSend} from "./IPCHandler"; import { IPCListenChannels, NFCAccountCard, NFCCard, NFCCardType, NFCPropertyCard, NFCTaskCard, PropertyColor, TaskType } from "./RawConstants"; import * as fs from "fs"; import path from "path"; export default class NFCHandler { public static initPCSC() { const nfc = new NFC(); // optionally you can pass logger nfc.on('reader', reader => { console.log(`${reader.name} device attached`); // enable when you want to auto-process ISO 14443-4 tags (standard=TAG_ISO_14443_4) // when an ISO 14443-4 is detected, SELECT FILE command with the AID is issued // the response is available as card.data in the card event // see examples/basic.js line 17 for more info // reader.aid = 'F222222222'; reader.on('card', card => { // card is object containing following data // [always] String type: TAG_ISO_14443_3 (standard nfc tags like MIFARE) or TAG_ISO_14443_4 (Android HCE and others) // [always] String standard: same as type // [only TAG_ISO_14443_3] String uid: tag uid // [only TAG_ISO_14443_4] Buffer data: raw data from select APDU response console.log(`${reader.name} card detected`, card); let parsed = this.formClass(card.uid, card.data.toString("utf8")); if (parsed && parsed.isComplete) IPCSend(IPCListenChannels.NFC_CARD, {status: true, data: parsed.card}); IPCSend(IPCListenChannels.NFC_RAW, {status: !!parsed, data: parsed}); }); reader.on('card.off', card => { console.log(`${reader.name} card removed`, card); }); reader.on('error', err => { console.log(`${reader.name} an error occurred`, err); }); reader.on('end', () => { console.log(`${reader.name} device removed`); }); }); nfc.on('error', err => { console.log('an error occurred', err); }); } public static initTest() { fs.watchFile(path.resolve(process.cwd(), "nfc.test"), (curr, prev) => { let contents = fs.readFileSync(path.resolve(process.cwd(), "nfc.test")).toString("utf-8"); let parsed = this.formClass("xyz", contents); if (parsed) IPCSend(IPCListenChannels.NFC_CARD, {status: true, data: parsed.card}); IPCSend(IPCListenChannels.NFC_RAW, {status: parsed.isComplete, data: parsed.card}); console.log("Got NFC", parsed); }) } /** * Returns formed nfc card or null if nothing can be parsed * @param uid * @param raw */ public static formClass(uid: string, raw: string): formClassReturn | null { /* NFC-Card FULL CARD_TYPE|PROPERTIES - ACCOUNT - PROPERTY - TASK */ let preParsed: NFCCard = { uid: uid, raw: raw, cardType: NFCCardType.INVALID, }; try { let parsing = raw.split("|"); switch (parsing[0]) { case "PROPERTY": { preParsed.cardType = NFCCardType.PROPERTY; const splitProperty = parsing[1].split(";"); /* 0 Name 1 Color 2 Fullset Amount 3 Buy Value 4 Mortgage Value 5 Rent Object (split with ,) 0 Basic rent [1 Rent with 1 house 2 Rent with 2 houses 3 Rent with 3 houses 4 Rent with 4 houses 5 Rent with 1 hotel] 6 Special Props "" "TRAINSTATION" "UTILITY" */ let name = this.valueOr(splitProperty, 0, ""); let color = this.valueOr(splitProperty, 1, PropertyColor.WHITE); let fullSetAmount = this.valueOr(splitProperty, 2, 0); let buyValue = this.valueOr(splitProperty, 3, 0); let mortgageValue = this.valueOr(splitProperty, 4, 0); let rent = 0; let rent1 = 0; let rent2 = 0; let rent3 = 0; let rent4 = 0; let rentHotel = 0; if (splitProperty[5]) { let rentSplit = splitProperty[5].split(","); rent = this.valueOr(rentSplit, 0, 0); rent1 = this.valueOr(rentSplit, 1, undefined); rent2 = this.valueOr(rentSplit, 2, undefined); rent3 = this.valueOr(rentSplit, 3, undefined); rent4 = this.valueOr(rentSplit, 4, undefined); rentHotel = this.valueOr(rentSplit, 5, undefined); } let specialProperties = this.valueOr(splitProperty, 6, ""); let propertyParsed: NFCPropertyCard = { ...preParsed, name: name, color: color, fullSetAmount: fullSetAmount, buyValue: buyValue, mortgageValue: mortgageValue, rent: rent, rent1: rent1, rent2: rent2, rent3: rent3, rent4: rent4, rentHotel: rentHotel, specialProperties: specialProperties, }; if (splitProperty.length != 7) return {card: propertyParsed, isComplete: false}; return {card: propertyParsed, isComplete: true}; } case "ACCOUNT": { preParsed.cardType = NFCCardType.ACCOUNT; const splitAccount = parsing[1].split(";"); let symbol = this.valueOr(splitAccount, 0, ""); let nickname = this.valueOr(splitAccount, 1, ""); let pin = this.valueOr(splitAccount, 2, 0); const splitParsed: NFCAccountCard = { ...preParsed, symbol: symbol, nickname: nickname, pin: Number.parseInt(pin) } if (splitAccount.length != 3) return {card: splitParsed, isComplete: false}; return {card: splitParsed, isComplete: true} } case "TASK": { preParsed.cardType = NFCCardType.ACCOUNT; const splitTask = parsing[1].split(";"); let taskType = this.valueOr(splitTask, 0, ""); let taskAmount1 = this.valueOr(splitTask, 1, 0); let taskAmount2 = this.valueOr(splitTask, 2, 0); const splitParsed: NFCTaskCard = { ...preParsed, type: taskType as TaskType, amount: Number.parseInt(taskAmount1) || 0, amount2: Number.parseInt(taskAmount2) || 0 } if (splitTask.length < 1 || splitTask.length > 3) return {card: splitParsed, isComplete: false}; return {card: splitParsed, isComplete: true} } default: { return {card: preParsed, isComplete: false}; } } } catch (e) { return null; } } private static valueOr(arr: string[], index: number, def: any) { try { if (arr[index]) return arr[index]; return def; } catch (e) { return def; } } } interface formClassReturn { card: NFCCard; isComplete: boolean; }