') + ')', 'gi'); if (regex.test(text)) { found = true; var frag = document.createDocumentFragment(); var parts = text.split(regex); parts.forEach(function(part, i) { if (i % 2 === 0) { frag.appendChild(document.createTextNode(part)); } else { var span = document.createElement('span'); span.className = 'userscript-highlight'; span.textContent = part; frag.appendChild(span); } }); node.parentNode.replaceChild(frag, node); } }); } else if (node.nodeType === 1 && node.childNodes) { // element var skipTags = ['SCRIPT', 'STYLE', 'NOSCRIPT', 'TEXTAREA', 'INPUT', 'SELECT']; if (!skipTags.includes(node.tagName)) { Array.from(node.childNodes).forEach(highlight); } } } highlight(document.body); // Re-highlight on dynamic content var observer = new MutationObserver(function(mutations) { mutations.forEach(function(m) { m.addedNodes.forEach(function(node) { if (node.nodeType === 1 || node.nodeType === 3) highlight(node); }); }); }); observer.observe(document.body, { childList: true, subtree: true }); })(); } } catch(__e) { console.warn('[Userscript:Highlight Search Terms]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + ', 'i'); if (__m === '*' || __re.test(location.href)) { // Strip utm_, fbclid, gclid, etc. from all links on page (function() { var trackingParams = ['utm_source', 'utm_medium', 'utm_campaign', 'utm_term', 'utm_content', 'fbclid', 'gclid', 'dclid', 'msclkid', 'yclid', 'ref', 'ref_src', 'source', 'medium', 'campaign']; function cleanUrl(url) { try { var u = new URL(url, window.location.origin); var changed = false; trackingParams.forEach(function(p) { if (u.searchParams.has(p)) { u.searchParams.delete(p); changed = true; } }); return changed ? u.toString() : url; } catch (e) { return url; } } function cleanLinks() { document.querySelectorAll('a[href]').forEach(function(a) { var clean = cleanUrl(a.href); if (clean !== a.href) a.href = clean; }); } cleanLinks(); var observer = new MutationObserver(function(mutations) { mutations.forEach(function(m) { m.addedNodes.forEach(function(node) { if (node.nodeType === 1) { if (node.tagName === 'A') cleanLinks(); node.querySelectorAll('a[href]').forEach(function(a) { var clean = cleanUrl(a.href); if (clean !== a.href) a.href = clean; }); } }); }); }); observer.observe(document.body, { childList: true, subtree: true }); })(); } } catch(__e) { console.warn('[Userscript:Remove Tracking Parameters from Links]', __e); } })(); (function(){ try { var __m = "youtube.com"; var __re = new RegExp('^' + "youtube\\.com" + ', 'i'); if (__m === '*' || __re.test(location.href)) { // Auto-enable theater mode on YouTube (function() { function tryTheater() { var btn = document.querySelector('button[aria-label="Theater mode"], ytd-player #player button[title="Theater mode"]'); if (btn && !btn.classList.contains('activated')) { btn.click(); } } // Try immediately tryTheater(); // Try after navigation (SPA) var lastUrl = location.href; setInterval(function() { if (location.href !== lastUrl) { lastUrl = location.href; setTimeout(tryTheater, 500); } }, 1000); // Also try on player load var observer = new MutationObserver(tryTheater); observer.observe(document.body, { childList: true, subtree: true }); })(); } } catch(__e) { console.warn('[Userscript:YouTube Theater Mode Default]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + ', 'i'); if (__m === '*' || __re.test(location.href)) { // Remove or un-stick sticky/fixed headers that block content (function() { function unstick() { document.querySelectorAll('header, nav, [role="banner"], .header, .navbar, .sticky, .fixed-top, [style*="position: fixed"], [style*="position:sticky"]').forEach(function(el) { if (el.style.position === 'fixed' || el.style.position === 'sticky' || getComputedStyle(el).position === 'fixed' || getComputedStyle(el).position === 'sticky') { el.style.position = 'static'; el.style.top = 'auto'; el.style.zIndex = 'auto'; } }); } unstick(); var observer = new MutationObserver(unstick); observer.observe(document.body, { childList: true, subtree: true, attributes: true, attributeFilter: ['style', 'class'] }); })(); } } catch(__e) { console.warn('[Userscript:Kill Sticky Headers]', __e); } })(); })(); GitHub - TapTrack/TappyTcmpJs: Javascript driver for using Tappies that communicate via the TCMP protocol · GitHub
Skip to content

Repository files navigation

Driver for working with Tappy devices that use the TCMP protocol.

Installation

NPM

npm install @taptrack/tappy

Bower

bower install tappy-tcmp

Connecting to Tappies

In order to connect to a Tappy, you must supply it with a TappyCommunicator.

NodeJS

For communicating with TappyUSB devices using the Node serialport driver, use the TappyNodeSerialCommunicator found under @taptrack/tappy-nodeserialcommunicator.

varcomm=newNodeSerialCommunicator({path: "/dev/ttyUSB0"});vartappy=newTappy({communicator: comm});tappy.connect(function(){console.log("Connected");tappy.disconnect(function(){console.log("Disconnected");});});

Chrome Packaged Apps

For communicating with TappyUSB devices connected in a Chrome packaged app, use the TappyChromeSerialCommunicator package found under @taptrack/tappy-chromeserialcommiunicator or tappy-chromeserialcommunicator depending on whether you are using NPM or Bower.

varpath="/dev/ttyUSB0";varcomm=newTappyChromeSerialCommunicator(path);vartappy=newTappy({communicator: comm});tappy.connect(function(){console.log("Connected");tappy.disconnect(function(){console.log("Disconnected");});});

Sending Commands

Once you have connected to a Tappy, you can send it TCMP commands. There are several families of commands supported by Tappies, but the System family and the BasicNFC family are the most commonly used.

varcommand=newTappySystemFamily.Commands.Ping();// A command must implement // getCommandCode() -> integer (0-255)// getCommandFamily() -> Uint8Array (length: 2)// getPayload() -> UintArray (length: 0-65530)tappy.sendMessage(command);

Receiving Commands

In order to receive communication from the Tappy, you must register a message listener.

varlistener=function(msg){// msg will be an object that implements:// getCommandFamily() returning a Uint8Array of length 2// getCommandCode() returning a 0-255 value// getPayload() returning a Uint8Array of length 0->n};tappy.setMessageListener(listener);// Can also be set when you initialize the tappy// var tappy = new Tappy({communicator: comm, messageListener: listener});

Detecting Errors

Errors that the Tappy experiences will be reported as standard responses using response codes specified in their command family definitions. Similarly, client communication errors will be reported as System family responses. However, if the driver detects a serial port error or receives an unparsable message from the Tappy, it reports the occurrence through a second callback.

varlistener=function(errorType,data){// errorTypes: found in Tappy.ErrorType, described below// data: either an object containing further information// or nothing in the case of NOT_CONNECTED};tappy.setErrorListener(listener);// Can also be set when you initialize the tappy:// var tappy = new Tappy({communicator: comm, errorListener: listener});

Error Types

  • Tappy.ErrorType.NOT_CONNECTED Attempted to send a message when communicator was in an unconnected state.

  • Tappy.ErrorType.CONNECTION_ERROR Communicator reported that an error occured when message send was attempted.

  • Tappy.ErrorType.INVALID_HDLC Data was received that violates the Tappy framing convention. This generally occurs because a control byte was found in the wrong place, perhaps due to communication bit corruption.

  • Tappy.ErrorType.INVALID_TCMP Data was received that used the correct Tappy HDLC framing, but the contents were not parsable as a valid TCMP message.

Methods

Tappy.resolveTagType(id)

Resolve a standard Tappy tag type numeric identifier into an object describing the properties of the tag.

id (required) Numeric tag id as returned by responses such as TagFound and TagWritten responses in the BasicNFC command family. The return value will be an object containing information about the type of tag that the Tappy is reporting.

Example result object for an NXP MIFARE DESFire EV1 8k:

{// Tappy standard tag identifier
id: 13;// NFC Forum Tag Type,// -1 for non-NFC forum compliant// 0 if unknown
forumType: 4;// Human-readable description of the// tag technology
description: "MIFARE DESFire EV1 8k";// Read/write user memory capacity of// the lowest capacity tag fitting // this type, 0 if there is insufficient// information to provide a useful number
safeCapacity: 8192;// Read/write user memory capacity of// the largest capacity tag fitting // this type, 0 if there is insufficient// information to provide a useful number
maxCapacity: 8192;}

It should be noted that the Tappy cannot always fully determine the type of tag it is being presented with. Unfortunately, the NFC tag detection procedure does not usually provide sufficient information to narrow a tag down to one tag technology. Therefore, the Tappy has the capability to deploy several heuristics in order to better determine what type of tag is connected, but making use of these heuristics significantly increases the number of operations performed on the tag during detection, so they can noticeably impair scanning performance.

As such, often the Tappy will report "Generic" tag types. In these cases the safeCapacity parameter will provide the user data capacity of the smallest tag likely to fit what the Tappy has detected, while maxCapacity has the capacity of the largest common tag the Tappy believes will fit the detection results. Note that these capacities are often uncertain and ignore uncommon tag technologies. Therefore, they should be treated as suggestions, not as a source of absolute truth.

Tappy(params)

Create a new Tappy object to communicate with a Tappy device.

params (required)

  • communicator (required) A Tappy communicator for this Tappy object to use to talk to the Tappy device. See TappyNodeSerialCommunicator and TappyChromeSerialCommunicatorJs

  • messageListener (optional) Initial message listener callback, see setMessageListener

  • errorListener (optional) Initial error listener callback, see setErrorListener

.setMessageListener(callback)

Sets the message listener for this Tappy. Note that a Tappy object can only have one message listener at a time, so calling this will replace the previous listener.

callback (required) Callback should be of the form function(msg) { ... }

msg TCMP message with the following methods:

  • getCommandFamily() -> Uint8Array 2 byte command family ID
  • getCommandCode() -> integer numerical command code 0-255
  • getPayload() -> Uint8Array the payload the packet contained (may be of length 0)

.setErrorListener(callback)

Sets the error listener for this Tappy. Note that a Tappy object can only have one error listener at a time, so calling this will replace the previous litener.

callback (required) Callback should be of the form function(errorType, data)

errorType One of values in Tappy.ErrorType

data (optional) Additional content describing the error. The contents and format of this object are not consistent. For instance, in the case of NOT_CONNECTED no data is passed, while in the case of a CONNECTION_ERROR, data will have contents that vary depending on the communicator in use.

.sendMessage(message)

Send a message to the Tappy. This method will throw if you attempt to send a message that does not implement the appropriate methods. If a send is attempted when the Tappy's communicator is not connected, the message will be discarded and the current error listener will be called with an error of NOT_CONNECTED.

message (required) The message must implement the following methods:

  • getCommandFamily() -> Uint8Array 2 byte command family ID
  • getCommandCode() -> integer numerical command code 0-255
  • getPayload() -> Uint8Array the payload the packet contained (may be of length 0)

.connect(callback)

Informs the communicator to open the connection to the Tappy it handles. Currently this is just a passthrough to the corresponding method on the communicator, but this characteristic may not be persisted in the future, so connect() should generally be called on the Tappy, not the backing communicator.

callback (optional) The callback is passed straight through to the communicator currently, so the connect callback format of the relevant communicator should be consulted for additional information.

.disconnect(callback)

Informs the communicator to close the connection to the Tappy it handles. Currently this is just a passthrough to the corresponding method on the communicator, but this characteristic may not be persisted in the future, so disconnect() should generally be called on the Tappy, not the backing communicator.

callback (optional) The callback is passed straight through to the communicator currently, so the disconnect callback format of the relevant communicator should be consulted for additional information.

.isConnected()

Returns true if the Tappy is connected. Currently, this is just a passthrough to the corresponding method on the communicator, but this characteristic may not be persisted in the future, so isConnected() should generally be called on the Tappy, not the backing communicator.

About

Javascript driver for using Tappies that communicate via the TCMP protocol

Resources

Stars

2 stars

Watchers

1 watching

Forks

Releases

Packages

Used by

Contributors

Languages