lifxlan
Version:
TypeScript library for controlling LIFX products over LAN
80 lines • 3.07 kB
JavaScript
import { decodeHeader, getPayload } from './encoding.js';
import { convertTargetToSerialNumber } from './utils/index.js';
import { SourceExhaustionError, ValidationError } from './errors.js';
const MAX_SOURCE = 0xFFFFFFFF;
/** 0 and 1 are reserved */
const MAX_SOURCE_VALUES = MAX_SOURCE - 2;
/**
* Since a client is a source of messages and there may be multiple
* clients sending messages at the same time, the router receives
* response messages and routes them to the client (a MessageHandler)
* that sent the request message.
*
* It has the added benefit of allowing callers to send messages
* messages. With the previous benefit, the router helps associate
* received messages with previously sent messages.
*
* It also decodes the header and converts the target to a serial
* number string. This helps avoid calling the relatively expensive
* convertTargetToSerialNumber utility function multiple times for
* each response message.
*/
export function Router(options) {
const handlers = options.handlers ?? new Map();
let sourceCounter = 2;
return {
nextSource() {
let source = -1;
for (let i = 0; i < MAX_SOURCE_VALUES; i++) {
if (!handlers.has(sourceCounter)) {
source = sourceCounter;
break;
}
sourceCounter = (sourceCounter + 1) % 0x100000000;
if (sourceCounter <= 1) {
sourceCounter = 2;
}
}
if (source === -1) {
throw new SourceExhaustionError();
}
return source;
},
register(source, handler) {
if (source <= 1 || source > MAX_SOURCE) {
throw new ValidationError('source', source, 'must be between 2 and 4294967295');
}
if (handlers.has(source)) {
throw new ValidationError('source', source, 'already registered');
}
handlers.set(source, handler);
},
deregister(source, handler) {
if (handlers.get(source) !== handler) {
throw new ValidationError('messageHandler', handler, 'does not match registered handler');
}
handlers.delete(source);
},
send(message, port, address, serialNumber) {
options.onSend(message, port, address, serialNumber);
},
receive(message) {
const header = decodeHeader(message);
const payload = getPayload(message);
const serialNumber = convertTargetToSerialNumber(header.target);
const handler = handlers.get(header.source);
if (handler) {
handler(header, payload, serialNumber);
}
if (options.onMessage) {
options.onMessage(header, payload, serialNumber);
}
return {
header,
payload,
serialNumber,
};
},
};
}
//# sourceMappingURL=router.js.map