uhppoted
Version:
NodeJS module wrapper for the interface to UHPPOTE TCP/IP Wiegand Access Controllers
853 lines (772 loc) • 21.6 kB
JavaScript
module.exports = {
/**
* Decodes the response to a get-status request (function code 0x20).
*
* @param {buffer} bytes 64 byte array
* @return {object} Decoded get-status response object
*/
GetStatus: function (bytes) {
const lookup = require('./lookup.js')
const evt = {
index: uint32(bytes, 8),
type: lookup.eventType(bytes, 12),
granted: bool(bytes, 13),
door: uint8(bytes, 14),
direction: lookup.direction(bytes, 15),
card: uint32(bytes, 16),
timestamp: optionalTimestamp(bytes, 20),
reason: lookup.reason(bytes, 27),
}
return {
deviceId: uint32(bytes, 4),
state: {
serialNumber: uint32(bytes, 4),
event: evt.index === 0 ? { index: 0 } : evt,
doors: {
1: bool(bytes, 28),
2: bool(bytes, 29),
3: bool(bytes, 30),
4: bool(bytes, 31),
},
buttons: {
1: bool(bytes, 32),
2: bool(bytes, 33),
3: bool(bytes, 34),
4: bool(bytes, 35),
},
system: {
status: uint8(bytes, 36),
date: yymmdd(bytes, 51),
time: HHmmss(bytes, 37),
},
specialInfo: uint8(bytes, 48),
relays: lookup.relays(bytes, 49),
inputs: lookup.inputs(bytes, 50),
},
}
},
/**
* Decodes the response to a set-time request (function code 0x30).
*
* @param {buffer} bytes 64 byte array
*
* @return {object} Decoded set-time response object
*/
SetTime: function (bytes) {
return {
deviceId: uint32(bytes, 4),
datetime: yyyymmddHHmmss(bytes, 8),
}
},
/**
* Decodes the response to a get-time request (function code 0x32).
*
* @param {buffer} bytes 64 byte array
*
* @return {object} Decoded get-time response object
*/
GetTime: function (bytes) {
return {
deviceId: uint32(bytes, 4),
datetime: yyyymmddHHmmss(bytes, 8),
}
},
/**
* Decodes the response to a put-card request (function code 0x50).
*
* @param {buffer} bytes 64 byte array
*
* @return {object} Decoded put-card response object
*/
PutCard: function (bytes) {
return {
deviceId: uint32(bytes, 4),
stored: bool(bytes, 8),
}
},
/**
* Decodes the response to a delete-card request (function code 0x52).
*
* @param {buffer} bytes 64 byte array
*
* @param {object} Decoded delete-card response object
*/
DeleteCard: function (bytes) {
return {
deviceId: uint32(bytes, 4),
deleted: bool(bytes, 8),
}
},
/**
* Decodes the response to a delete-cards request (function code 0x54).
*
* @param {buffer} bytes 64 byte array
*
* @param {object} Decoded delete-cards response object
*/
DeleteCards: function (bytes) {
return {
deviceId: uint32(bytes, 4),
deleted: bool(bytes, 8),
}
},
/**
* Decodes the response to a get-cards request (function code 0x58).
*
* @param {buffer} bytes 64 byte array
*
* @param {object} Decoded get-cards response object
*/
GetCards: function (bytes) {
return {
deviceId: uint32(bytes, 4),
cards: uint32(bytes, 8),
}
},
/**
* Decodes the response to a get-card-by-id request (function code 0x5a).
*
* @param {buffer} bytes 64 byte array
*
* @param {object} Decoded get-card-by-id response object
*/
GetCardByID: function (bytes) {
const doors = {}
const offset = 20
;['1', '2', '3', '4'].forEach((door, index) => {
const permission = uint8(bytes, offset + index)
if (permission === 0) {
doors[door] = false
} else if (permission === 1) {
doors[door] = true
} else if (permission >= 2 && permission <= 254) {
doors[door] = permission
}
})
return {
deviceId: uint32(bytes, 4),
card: {
number: uint32(bytes, 8),
valid: {
from: yyyymmdd(bytes, 12),
to: yyyymmdd(bytes, 16),
},
doors,
PIN: uint24(bytes, 24),
},
}
},
/**
* Decodes the response to a get-card-by-index request (function code 0x5c).
*
* @param {buffer} bytes 64 byte array
*
* @param {object} Decoded get-card-by-index response object
*/
GetCardByIndex: function (bytes) {
const doors = {}
const offset = 20
;['1', '2', '3', '4'].forEach((door, index) => {
const permission = uint8(bytes, offset + index)
if (permission === 0) {
doors[door] = false
} else if (permission === 1) {
doors[door] = true
} else if (permission >= 2 && permission <= 254) {
doors[door] = permission
}
})
return {
deviceId: uint32(bytes, 4),
card: {
number: uint32(bytes, 8),
valid: {
from: yyyymmdd(bytes, 12),
to: yyyymmdd(bytes, 16),
},
doors,
PIN: uint24(bytes, 24),
},
}
},
/**
* Decodes the response to a set-door-control request (function code 0x80).
*
* @param {buffer} bytes 64 byte array
*
* @param {object} Decoded set-door-control response object
*/
SetDoorControl: function (bytes) {
const lookup = require('./lookup.js')
return {
deviceId: uint32(bytes, 4),
doorControlState: {
door: uint8(bytes, 8),
delay: uint8(bytes, 10),
control: lookup.doorState(bytes, 9),
},
}
},
/**
* Decodes the response to a get-door-control request (function code 0x82).
*
* @param {buffer} bytes 64 byte array
*
* @param {object} Decoded get-door-control response object
*/
GetDoorControl: function (bytes) {
const lookup = require('./lookup.js')
return {
deviceId: uint32(bytes, 4),
doorControlState: {
door: uint8(bytes, 8),
delay: uint8(bytes, 10),
control: lookup.doorState(bytes, 9),
},
}
},
/**
* Decodes the response to a set-listener request (function code 0x90).
*
* @param {buffer} bytes 64 byte array
*
* @param {object} Decoded set-listener response object
*/
SetListener: function (bytes) {
return {
deviceId: uint32(bytes, 4),
updated: bool(bytes, 8),
}
},
/**
* Decodes the response to a get-listener request (function code 0x92).
*
* @param {buffer} bytes 64 byte array
*
* @param {object} Decoded get-listener response object
*/
GetListener: function (bytes) {
return {
deviceId: uint32(bytes, 4),
address: address(bytes, 8),
port: uint16(bytes, 12),
interval: uint8(bytes, 14),
}
},
/**
* Decodes the response to a get-device request (function code 0x94).
*
* @param {buffer} buffer 64 byte NodeJS buffer
*
* @param {object} Decoded device object
*/
GetDevice: function (bytes) {
return {
deviceId: uint32(bytes, 4),
device: {
serialNumber: uint32(bytes, 4),
address: address(bytes, 8),
netmask: address(bytes, 12),
gateway: address(bytes, 16),
MAC: mac(bytes, 20),
version: bcd(bytes, 26, 2),
date: yyyymmdd(bytes, 28),
},
}
},
/**
* Decodes the response to a get-time-profile request (function code 0x98).
*
* @param {buffer} buffer 64 byte NodeJS buffer
* @param {function} translator (optional) function to internationalise the text in a
* decoded object
*
* @param {object} Decoded response to a get-time-profile object
*/
GetTimeProfile: function (bytes, _translator) {
const map = new Map([
[17, 'Monday'],
[18, 'Tuesday'],
[19, 'Wednesday'],
[20, 'Thursday'],
[21, 'Friday'],
[22, 'Saturday'],
[23, 'Sunday'],
])
let profile = null
const profileID = uint8(bytes, 8)
if (profileID !== 0) {
const weekdays = []
const segments = []
map.forEach((v, k) => {
if (bool(bytes, k)) {
weekdays.push(v)
}
})
let offset = 24
for (let i = 0; i < 3; i++) {
const start = HHmm(bytes, offset)
const end = HHmm(bytes, offset + 2)
if (start !== '00:00' || end !== '00:00') {
segments.push({ start, end })
}
offset = offset + 4
}
const linked = uint8(bytes, 36)
profile = {
id: profileID,
valid: {
from: yyyymmdd(bytes, 9),
to: yyyymmdd(bytes, 13),
},
weekdays,
segments,
}
if (linked !== 0) {
profile.linkedTo = linked
}
}
return {
deviceId: uint32(bytes, 4),
profile,
}
},
/**
* Decodes the response to a set-time-profile request (function code 0x88).
*
* @param {buffer} buffer 64 byte NodeJS buffer
* @param {function} translator (optional) function to internationalise the text in a
* decoded object
*
* @param {object} Decoded set-time-profile response object
*/
SetTimeProfile: function (bytes, _translator) {
return {
deviceId: uint32(bytes, 4),
updated: bool(bytes, 8),
}
},
/**
* Decodes the response to a clear-time-profiles request (function code 0x8a).
*
* @param {buffer} buffer 64 byte NodeJS buffer
* @param {function} translator (optional) function to internationalise the text in a
* decoded object
*
* @param {object} Decoded clear-time-profiles response object
*/
ClearTimeProfiles: function (bytes, _translator) {
return {
deviceId: uint32(bytes, 4),
cleared: bool(bytes, 8),
}
},
/**
* Decodes the response to a clear-task-list request (function code 0xa6).
*
* @param {buffer} buffer 64 byte NodeJS buffer
* @param {function} translator (optional) function to internationalise the text in a
* decoded object
*
* @param {object} Decoded clear-task-list response object
*/
ClearTaskList: function (bytes, _translator) {
return {
deviceId: uint32(bytes, 4),
cleared: bool(bytes, 8),
}
},
/**
* Decodes the response to an add-task request (function code 0xa8).
*
* @param {buffer} buffer 64 byte NodeJS buffer
* @param {function} translator (optional) function to internationalise the text in a
* decoded object
*
* @param {object} Decoded add-task response object
*/
AddTask: function (bytes, _translator) {
return {
deviceId: uint32(bytes, 4),
added: bool(bytes, 8),
}
},
/**
* Decodes the response to a refresh-task-list request (function code 0xac).
*
* @param {buffer} buffer 64 byte NodeJS buffer
* @param {function} translator (optional) function to internationalise the text in a
* decoded object
*
* @param {object} Decoded refresh-task-list response object
*/
RefreshTaskList: function (bytes, _translator) {
return {
deviceId: uint32(bytes, 4),
refreshed: bool(bytes, 8),
}
},
/**
* Decodes the response to a record-special-events request (function code 0x8e).
*
* @param {buffer} buffer 64 byte NodeJS buffer
*
* @param {object} Decoded record-special-events response object
*/
RecordSpecialEvents: function (bytes) {
return {
deviceId: uint32(bytes, 4),
updated: bool(bytes, 8),
}
},
/**
* Decodes the response to a get-event request (function code 0xb0).
*
* @param {buffer} buffer 64 byte NodeJS buffer
*
* @param {object} Decoded event object
*/
GetEvent: function (bytes) {
const lookup = require('./lookup.js')
return {
deviceId: uint32(bytes, 4),
event: {
index: uint32(bytes, 8),
type: lookup.eventType(bytes, 12),
granted: bool(bytes, 13),
door: uint8(bytes, 14),
direction: lookup.direction(bytes, 15),
card: uint32(bytes, 16),
timestamp: optionalTimestamp(bytes, 20),
reason: lookup.reason(bytes, 27),
},
}
},
/**
* Decodes the response to a set-event-index request (function code 0xb2).
*
* @param {buffer} buffer 64 byte NodeJS buffer
*
* @param {object} Decoded set-event-index response object
*/
SetEventIndex: function (bytes) {
return {
deviceId: uint32(bytes, 4),
updated: bool(bytes, 8),
}
},
/**
* Decodes the response to a get-event-index request (function code 0xb4).
*
* @param {buffer} buffer 64 byte NodeJS buffer
*
* @param {object} Decoded get-event-index response object
*/
GetEventIndex: function (bytes) {
return {
deviceId: uint32(bytes, 4),
index: uint32(bytes, 8),
}
},
/**
* Decodes the response to an open-door request (function code 0x40).
*
* @param {buffer} bytes 64 byte array
*
* @return {object} Decoded open-door response object
*/
OpenDoor: function (bytes) {
return {
deviceId: uint32(bytes, 4),
opened: bool(bytes, 8),
}
},
/**
* Decodes the response to a set-pc-control request (function code 0xa0).
*
* @param {buffer} buffer 64 byte NodeJS buffer
*
* @param {object} Decoded set-pc-control response object
*/
SetPCControl: function (bytes) {
return {
deviceId: uint32(bytes, 4),
ok: bool(bytes, 8),
}
},
/**
* Decodes the response to a set-interlock request (function code 0xa2).
*
* @param {buffer} buffer 64 byte NodeJS buffer
*
* @param {object} Decoded set-interlock response object
*/
SetInterlock: function (bytes) {
return {
deviceId: uint32(bytes, 4),
ok: bool(bytes, 8),
}
},
/**
* Decodes the response to an activate-keypads request (function code 0xa4).
*
* @param {buffer} buffer 64 byte NodeJS buffer
*
* @param {object} Decoded acivate-keypads response object
*/
ActivateKeypads: function (bytes) {
return {
deviceId: uint32(bytes, 4),
ok: bool(bytes, 8),
}
},
/**
* Decodes the response to a set-door-passcodes request (function code 0x8x).
*
* @param {buffer} buffer 64 byte NodeJS buffer
*
* @param {object} Decoded set-super-passwords response object
*/
SetDoorPasscodes: function (bytes) {
return {
deviceId: uint32(bytes, 4),
ok: bool(bytes, 8),
}
},
/**
* Decodes the response to a restore-default-parameters request (function code 0xc8).
*
* @param {buffer} bytes 64 byte array
*
* @param {object} Decoded restore-default-parameters response object
*/
RestoreDefaultParameters: function (bytes) {
return {
deviceId: uint32(bytes, 4),
ok: bool(bytes, 8),
}
},
}
/**
* Internal utility function to extract a bool from a response message.
*
* @param {array} buffer 64 byte DataView
* @param {number} offset Index of bool in buffer
*
* @param {bool} true if the byte at the offset is 1, false otherwise.
* @private
*/
function bool(bytes, offset) {
return bytes.getUint8(offset, true) === 0x01
}
/**
* Internal utility function to extract a uint8 from a response message.
*
* @param {array} buffer 64 byte DataView
* @param {number} offset Index of uint8 in buffer
*
* @param {uint8} uint8 at offset in buffer.
* @private
*/
function uint8(bytes, offset) {
return bytes.getUint8(offset, true)
}
/**
* Internal utility function to extract a uint16 from a response message.
*
* @param {array} buffer 64 byte DataView
* @param {number} offset Index of uint16 in buffer
*
* @param {uint16} Litte-endian uint16 at offset in buffer.
* @private
*/
function uint16(bytes, offset) {
return bytes.getUint16(offset, true)
}
/**
* Internal utility function to extract a uint24 from a response message.
*
* @param {array} buffer 64 byte DataView
* @param {number} offset Index of uint24 in buffer
*
* @param {uint32} Litte-endian uint24 at offset in buffer.
*/
function uint24(bytes, offset) {
let v = 0
v |= bytes.getUint8(offset + 2, true) & 0x00ff
v <<= 8
v |= bytes.getUint8(offset + 1, true) & 0x00ff
v <<= 8
v |= bytes.getUint8(offset, true) & 0x00ff
return v
}
/**
* Internal utility function to extract a uint32 from a response message.
*
* @param {array} buffer 64 byte DataView
* @param {number} offset Index of uint32 in buffer
*
* @param {uint32} Litte-endian uint32 at offset in buffer.
* @private
*/
function uint32(bytes, offset) {
return bytes.getUint32(offset, true)
}
/**
* Internal utility function to extract a BCD number from a response message.
*
* @param {array} buffer 64 byte DataView
* @param {number} offset Index of BCD number in buffer
* @param {number} length Number of bytes to decode
*
* @param {string} Decoded number as a string.
* @private
*/
function bcd(bytes, offset, length) {
const slice = new Uint8Array(bytes.buffer.slice(offset, offset + length))
const digits = []
for (let i = 0; i < slice.length; i++) {
digits.push((slice[i] >>> 4).toString(10))
digits.push((slice[i] & 0x0f).toString(10))
}
return digits.join('')
}
/**
* Internal utility function to extract a BCD timestamp from a response message.
*
* @param {array} buffer 64 byte DataView
* @param {number} offset Index of timestamp in buffer
*
* @param {string} Decoded 6 byte timestamp in yyy-mm-dd HH:mm:ss format.
* @private
*/
function yyyymmddHHmmss(bytes, offset) {
const datetime = bcd(bytes, offset, 7)
const date =
datetime.substr(0, 4) +
'-' +
datetime.substr(4, 2) +
'-' +
datetime.substr(6, 2)
const time =
datetime.substr(8, 2) +
':' +
datetime.substr(10, 2) +
':' +
datetime.substr(12, 2)
return date + ' ' + time
}
/**
* Internal utility function to extract a BCD timestamp from a response message. Returns a
* blank string if the timestamp is '00000000000000'.
*
* @param {array} buffer 64 byte DataView
* @param {number} offset Index of timestamp in buffer
*
* @param {string} Decoded 6 byte timestamp in yyy-mm-dd HH:mm:ss format.
* @private
*/
function optionalTimestamp(bytes, offset) {
const datetime = bcd(bytes, offset, 7)
if (datetime === '00000000000000') {
return ''
} else {
const date =
datetime.substr(0, 4) +
'-' +
datetime.substr(4, 2) +
'-' +
datetime.substr(6, 2)
const time =
datetime.substr(8, 2) +
':' +
datetime.substr(10, 2) +
':' +
datetime.substr(12, 2)
return date + ' ' + time
}
}
/**
* Internal utility function to extract a BCD date from a response message.
*
* @param {array} buffer 64 byte DataView
* @param {number} offset Index of date in buffer
*
* @param {string} Decoded 4 byte date in yyyy-mm-dd format.
* @private
*/
function yyyymmdd(bytes, offset) {
const date = bcd(bytes, offset, 4)
if (date === '00000000') {
return ''
}
return date.substr(0, 4) + '-' + date.substr(4, 2) + '-' + date.substr(6, 2)
}
/**
* Internal utility function to extract an abbreviated BCD date from a response message.
*
* @param {array} buffer 64 byte DataView
* @param {number} offset Index of date in buffer
*
* @param {string} Decoded 3 byte date in yyyy-mm-dd format (assumes base centry is 2000).
* @private
*/
function yymmdd(bytes, offset) {
const date = '20' + bcd(bytes, offset, 3)
return date.substr(0, 4) + '-' + date.substr(4, 2) + '-' + date.substr(6, 2)
}
/**
* Internal utility function to extract a BCD segment start/end from a response message.
*
* @param {array} buffer 64 byte DataView
* @param {number} offset Index of time in buffer
*
* @param {string} Decoded 2 byte time in HH:mm format.
*/
function HHmm(bytes, offset) {
const time = bcd(bytes, offset, 2)
return time.substr(0, 2) + ':' + time.substr(2, 2)
}
/**
* Internal utility function to extract a BCD time from a response message.
*
* @param {array} buffer 64 byte DataView
* @param {number} offset Index of time in buffer
*
* @param {string} Decoded 3 byte time in HH:mm:ss format.
* @private
*/
function HHmmss(bytes, offset) {
const time = bcd(bytes, offset, 3)
return time.substr(0, 2) + ':' + time.substr(2, 2) + ':' + time.substr(4, 2)
}
/**
* Internal utility function to extract an IP address from a response message.
*
* @param {array} buffer 64 byte DataView
* @param {number} offset Index of IP address in buffer
*
* @param {string} Decoded 4 byte IPv4 address as a IP address object.
* @private
*/
function address(bytes, offset) {
const ipx = require('./ipx.js')
return ipx.fromLong(bytes.getUint32(offset, false))
}
/**
* Internal utility function to extract a MAC address from a response message.
*
* @param {array} buffer 64 byte DataView
* @param {number} offset Index of MAC address in buffer
*
* @param {string} Decoded 6 byte MAC address as a colon delimited string.
* @private
*/
function mac(bytes, offset) {
const slice = new Uint8Array(bytes.buffer.slice(offset, offset + 6))
const hex = []
for (let i = 0; i < slice.length; i++) {
hex.push(('0' + slice[i].toString(16)).slice(-2))
}
return hex.join(':')
}