Skip to content

Commit

Permalink
WIP
Browse files Browse the repository at this point in the history
  • Loading branch information
NorthernMan54 committed Nov 28, 2024
1 parent 4acc68f commit e8e60b6
Show file tree
Hide file tree
Showing 7 changed files with 221 additions and 75 deletions.
8 changes: 4 additions & 4 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -48,7 +48,7 @@
"dependencies": {
"better-queue": ">=3.8.12",
"debug": "^4.3.5",
"@homebridge/hap-client": "2.0.5-beta.5"
"@homebridge/hap-client": "2.0.5-beta.7"
},
"author": "NorthernMan54",
"license": "ISC",
Expand Down
2 changes: 1 addition & 1 deletion src/HapDeviceRoutes.js
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,7 @@ class HapDeviceRoutes {
getDeviceById(req, res, key) {
const devices = this.RED.nodes.getNode(req.params.id)?.[key];
if (devices) {
debug(`${key} devices`, devices.length);
// debug(`${key} devices`, devices.length);
res.send(devices);
} else {
res.status(404).send();
Expand Down
99 changes: 41 additions & 58 deletions src/hbConfigNode.js
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ class HBConfigNode {
if (!config.jest) {
RED.nodes.createNode(this, config);

// Initialize properties
this.username = config.username;
this.macAddress = config.macAddress || '';
this.users = {};
Expand All @@ -19,6 +20,7 @@ class HBConfigNode {
this.monitorNodes = [];
this.log = new Log(console, true);

// Initialize queue
this.reqisterQueue = new Queue(this._register.bind(this), {
concurrent: 1,
autoResume: false,
Expand All @@ -29,27 +31,25 @@ class HBConfigNode {
});
this.reqisterQueue.pause();

// Initialize HAP client
this.hapClient = new HapClient({
config: { debug: false },
pin: config.username,
logger: this.log,
});

this.waitForNoMoreDiscoveries();
this.hapClient.on('instance-discovered', this.waitForNoMoreDiscoveries);

this.waitForNoMoreDiscoveries();
this.on('close', this.close.bind(this));
}
}

waitForNoMoreDiscoveries = () => {
if (this.discoveryTimeout) {
clearTimeout(this.discoveryTimeout);
}

clearTimeout(this.discoveryTimeout);
this.discoveryTimeout = setTimeout(() => {
this.log.debug('No more instances discovered, publishing services');
this.hapClient.removeListener('instance-discovered', this.waitForNoMoreDiscoveries);
this.hapClient.on('instance-discovered', async (instance) => { debug('instance-discovered', instance); await this.monitorDevices(); });
this.handleReady();
}, 5000);
};
Expand All @@ -58,10 +58,9 @@ class HBConfigNode {
this.hbDevices = await this.hapClient.getAllServices();
this.evDevices = this.toList({ perms: 'ev' });
this.ctDevices = this.toList({ perms: 'pw' });
console.log('evDevices', this.evDevices);
console.log('ctDevices', this.ctDevices);
this.log.info(`Devices initialized: evDevices: ${this.evDevices.length}, ctDevices: ${this.ctDevices.length}`);
this.handleDuplicates(this.evDevices);
debug('Queue', this.reqisterQueue.getStats());
debug('Queue stats:', this.reqisterQueue.getStats());
this.reqisterQueue.resume();
}

Expand Down Expand Up @@ -92,88 +91,72 @@ class HBConfigNode {
const seenFullNames = new Set();
const seenUniqueIds = new Set();

for (const endpoint of list) {
if (seenFullNames.has(endpoint.fullName)) {
list.forEach(endpoint => {
if (!seenFullNames.add(endpoint.fullName)) {
console.warn('WARNING: Duplicate device name', endpoint.fullName);
} else {
seenFullNames.add(endpoint.fullName);
}

if (seenUniqueIds.has(endpoint.uniqueId)) {
console.error('ERROR: Parsing failed, duplicate uniqueID.', endpoint.fullName);
} else {
seenUniqueIds.add(endpoint.uniqueId);
if (!seenUniqueIds.add(endpoint.uniqueId)) {
console.error('ERROR: Duplicate uniqueId detected.', endpoint.fullName);
}
}
});
}

register(clientNode) {
debug('Register %s -> %s', clientNode.type, clientNode.name);
debug('Register: %s type: %s', clientNode.type, clientNode.name);
this.clientNodes[clientNode.id] = clientNode;
this.reqisterQueue.push(clientNode);
clientNode.status({ fill: 'yellow', shape: 'ring', text: 'connecting' });
}

async _register(clientNodes, cb) {
for (const clientNode of clientNodes) {
debug('_Register %s -> %s', clientNode.type, clientNode.name);
clientNode.hbDevice = this.hbDevices.find(service => {
const deviceUnique = `${service.instance.name}${service.instance.username}${service.accessoryInformation.Manufacturer}${service.serviceName}${service.uuid.slice(0, 8)}`;
if (clientNode.device === deviceUnique) {
clientNode.status({ fill: 'green', shape: 'dot', text: 'connected' });
clientNode.emit('hbReady', service);
debug('_Register: %s type: %s', clientNode.type, clientNode.name);
const matchedDevice = this.hbDevices.find(service =>
clientNode.device === `${service.instance.name}${service.instance.username}${service.accessoryInformation.Manufacturer}${service.serviceName}${service.uuid.slice(0, 8)}`
);

if (matchedDevice) {
clientNode.hbDevice = matchedDevice;
clientNode.status({ fill: 'green', shape: 'dot', text: 'connected' });
clientNode.emit('hbReady', matchedDevice);
if (clientNode.config.type === 'hb-status') {
this.monitorNodes[clientNode.device] = matchedDevice;
}
return clientNode.device === deviceUnique;
});

if (clientNode.config.type === 'hb-status') {
this.monitorNodes[clientNode.device] = clientNode.hbDevice;
}

if (!clientNode.hbDevice) {
console.error('ERROR: _register - HB Device Missing', clientNode.name);
} else {
console.error('ERROR: Device registration failed', clientNode.name);
}
}

await this.monitorDevices();

cb(null);
}

async monitorDevices() {
if (Object.keys(this.monitorNodes).length) {
this.monitor = await this.hapClient.monitorCharacteristics(Object.values(this.monitorNodes));
this.monitor.on('service-update', (services) => {
for (const service of services) {
const eventNodes = Object.values(this.clientNodes).filter(
clientNode =>
clientNode.config.device === `${service.instance.name}${service.instance.username}${service.accessoryInformation.Manufacturer}${service.serviceName}${service.uuid.slice(0, 8)}`
services.forEach(service => {
const eventNodes = Object.values(this.clientNodes).filter(clientNode =>
clientNode.config.device === `${service.instance.name}${service.instance.username}${service.accessoryInformation.Manufacturer}${service.serviceName}${service.uuid.slice(0, 8)}`
);

eventNodes.forEach((eventNode) => {
if (eventNode._events && typeof eventNode.emit === 'function') {
eventNode.emit('hbEvent', service);
}
});
}
eventNodes.forEach(eventNode => eventNode.emit('hbEvent', service));
});
});
}
cb(null);
}
/*
deregister(clientNode) {
clientNode.status({ text: 'disconnected', shape: 'ring', fill: 'red' });
delete this.clientNodes[clientNode.id];
}
*/
close() {
if (this.hapClient) {
this.hapClient.destroy();
}
this.hapClient?.destroy();
}

}

// Cameras have multiple AID's of 1....

// Filter unique devices by AID, service name, username, and port
const filterUnique = (data) => {
const seen = new Set();
return data.filter(item => {
const uniqueKey = `${item.aid}-${item.serviceName}-${item.instance.username}-${item.instance.port}`;
console.log(uniqueKey, seen.has(uniqueKey))
if (seen.has(uniqueKey)) return false;
seen.add(uniqueKey);
return true;
Expand Down
1 change: 1 addition & 0 deletions src/hbControlNode.js
Original file line number Diff line number Diff line change
Expand Up @@ -54,6 +54,7 @@ class HbControlNode extends hbBaseNode {
const result = await this.hbDevice.setCharacteristicByType(key, message.payload[key]);
results.push({ [result.type]: result.value });
} catch (error) {
console.log(error)
this.error(`Failed to set value for "${key}": ${error.message}`);
results.push({ [key]: `Error: ${error.message}` });
fill = 'red';
Expand Down
18 changes: 12 additions & 6 deletions src/hbStatusNode.js
Original file line number Diff line number Diff line change
Expand Up @@ -15,13 +15,19 @@ class HbStatusNode extends HbBaseNode {
}

const result = await this.hbDevice.refreshCharacteristics();
this.status({
text: this.statusText(JSON.stringify(await this.hbDevice.values)),
shape: 'dot',
fill: 'green'
});
if (result) {
this.status({
text: this.statusText(JSON.stringify(await this.hbDevice.values)),
shape: 'dot',
fill: 'green'
});

send(Object.assign(message, this.createMessage(result)));
} else {
this.status({ fill: "red", shape: "ring", text: "disconnected" });
this.error("No response from device", this.name);
}

send(Object.assign(message, this.createMessage(result)));
}
}

Expand Down
Loading

0 comments on commit e8e60b6

Please sign in to comment.