homebridge / HAP-NodeJS

Node.js implementation of the HomeKit Accessory Protocol (HAP)
Apache License 2.0
2.68k stars 630 forks source link

Coding question and making work with HAP-NodeJS #715

Closed n0rt0nthec4t closed 4 years ago

n0rt0nthec4t commented 5 years ago

Not technically a HAP-NodeSJ issue, but I'm trying to make my accessories bit smart by doing auto discovery of devices on the network. I have a function, doDiscovery which does UDP scans and then depending on results, builds list of devices that response. Code Below:

My issue is, I need where commented // Need to wait some time to allow the on(message) event to process before proceeding to exit this function

So the function needs to wait say, 3-5seconds before it exist with a return

function doDisovery() { var foundDevices = []; var updQuery = "DAIKIN_UDP/common/basic_info"; var udpSocket = dgram.createSocket({type:"udp4", reuseAddr:true});

udpSocket.bind(30000, "0.0.0.0", function () {
    udpSocket.addMembership('224.0.0.1');
    udpSocket.setBroadcast(true);
});

udpSocket.on('message', function (chunk, info) {
    // Callback triggered when we've received a UDP response
    if (foundDevices.indexOf(info.address) == -1) {
        //doSomeProcessing
        foundDevices.push(info.address);
    }
});

udpSocket.on('listening', function() {
    // UDP socket opened fro listen, so send the queries
    udpSocket.send(updQuery, 0, updQuery.length, 30050, "255.255.255.255");
});

udpSocket.on('close', function() {
    console.log("closed");
});

// Need to wait some time to allow the on(message) event to process before proceeding to exit this function

udpSocket.close();
return foundDevices;

}

var devices = doDisovery();

NorthernMan54 commented 5 years ago

Very simple,

setTimeout(function () { console.log('timeout completed'); udpSocket.close(); return foundDevices; }, 5000);

n0rt0nthec4t commented 5 years ago

Wont the still mean the doDiscovery function will exit before the timeout is completed? So if you do a test after var devices = doDisovery() to see if teh array contains any data, it'll be null?

NorthernMan54 commented 5 years ago

Sorry, wasn't paying attention to the question. With javascript you need to use callbacks, which if your not familiar with is a steep learning curve.

function doDisovery(callback) {
var foundDevices = [];
var updQuery = "DAIKIN_UDP/common/basic_info";
var udpSocket = dgram.createSocket({type:"udp4", reuseAddr:true});

udpSocket.bind(30000, "0.0.0.0", function () {
    udpSocket.addMembership('224.0.0.1');
    udpSocket.setBroadcast(true);
});

udpSocket.on('message', function (chunk, info) {
    // Callback triggered when we've received a UDP response
    if (foundDevices.indexOf(info.address) == -1) {
        //doSomeProcessing
        foundDevices.push(info.address);
    }
});

udpSocket.on('listening', function() {
    // UDP socket opened fro listen, so send the queries
    udpSocket.send(updQuery, 0, updQuery.length, 30050, "255.255.255.255");
});

udpSocket.on('close', function() {
    console.log("closed");
});

// Need to wait some time to allow the on(message) event to process before proceeding to exit this function

setTimeout(function () {
console.log('timeout completed');
udpSocket.close();
callback(foundDevices);
}, 5000);
}

doDisovery(function(devices) { 
console.log('your devices', devices);
});
n0rt0nthec4t commented 5 years ago

Hrrmm.. This issue is still, that the code sequencing is wrong, so the HAP-NodeJS accessory loader exits before get the chance to create the accessories. doDiscovery callback gets called after the _accfactory.js code has exited full code below, so HPA-NodeJS doesn't know about them.

function doDiscovery(callback) { var foundDevices = []; var updQuery = "DAIKIN_UDP/common/basic_info"; var udpSocket = dgram.createSocket({type:"udp4", reuseAddr:true});

udpSocket.bind(30000, "0.0.0.0", function () {
    udpSocket.addMembership('224.0.0.1');
    udpSocket.setBroadcast(true);
});

udpSocket.on('message', function (chunk, info) {
    // Callback triggered when we've received a UDP response
        // Callback triggered when we've received a UDP response
    var daikinACInfo = JSON.parse(convertDaikinToJSON(chunk.toString('utf8')));
    if (foundDevices.indexOf(info.address) == -1) {
        // Have not found this Daikin before, so add to array of discovered systems
        foundDevices.push({IPaddress: info.address, MACaddress: daikinACInfo.mac.substr(0,2) + ":" + daikinACInfo.mac.substr(2,2) + ":" + daikinACInfo.mac.substr(4,2) + ":" + daikinACInfo.mac.substr(6,2) + ":" + daikinACInfo.mac.substr(8,2) + ":" + daikinACInfo.mac.substr(10,2), name: hex2ascii(daikinACInfo.name), version: daikinACInfo.ver.replace(/_/g, ".")});
    }
});

udpSocket.on('listening', function() {
    // UDP socket opened to listen, so send the queries
    udpSocket.send(updQuery, 0, updQuery.length, 30050, "255.255.255.255");
});

setTimeout(function () {
    udpSocket.close();
    callback(foundDevices);
}, 5000);

}

doDiscovery(function(devices) { var daikinACs = []; if (devices.length > 0) { for (var index in devices) {

        // Create the main airconditioner accessory and associated accessories 
        daikinACs[index] = new AirconditionerClass();
        daikinACs[index].__accessory = exports.accessory = new Accessory(AccessoryName, uuid.generate("hap-nodejs:accessories:daikin_"+devices[index].name));
        daikinACs[index].__accessory.username = devices[index].MACaddress;
        daikinACs[index].__accessory.pincode = AccessoryPincode;
        daikinACs[index].__accessory.category = Accessory.Categories.AIR_CONDITIONER;    // Air-conditioner type accessory
        daikinACs[index].__accessory.getService(Service.AccessoryInformation).setCharacteristic(Characteristic.Manufacturer, "Daikin");
        daikinACs[index].__accessory.getService(Service.AccessoryInformation).setCharacteristic(Characteristic.Model, "Some Model");
        daikinACs[index].__accessory.getService(Service.AccessoryInformation).setCharacteristic(Characteristic.SerialNumber, "Some Serial");
        daikinACs[index].__accessory.getService(Service.AccessoryInformation).setCharacteristic(Characteristic.FirmwareRevision, devices[index].version);

        daikinACs[index].__airconIP = devices[index].IPaddress;
        daikinACs[index].addAirconditioner(daikinACs[index].__accessory, devices[index].name, 1);
        daikinACs[index].addDehumidifier(devices[index].name, 1);
        daikinACs[index].addFan(devices[index].name, 1);
        daikinACs[index].addOutsideTempSensor(devices[index].name, 1);

        // Do update of current air-con system status for HomeKit, before setting refresh schedule
        daikinACs[index].__DaikinStatus();
        daikinACs[index].refreshHomeKit(2000);

        accessories.push(daikinACs[index].__accessory);   // Push onto export array for HAP-NodeJS "accessory factory"
    }
}

});

NorthernMan54 commented 5 years ago

Sorry can't help with that, I code my devices with the homebridge API

n0rt0nthec4t commented 5 years ago

Anyone have any further ideas how to achieve this?

Supereg commented 5 years ago

... so the HAP-NodeJS accessory loader exits before get the chance to create the accessories ...

I assume you are using the Core.ts/BridgedCore.ts and placing your file into the accessories folder? You could also create your program by using hap-nodejs as a dependency and publishing your accessories on your own/by hand. So there is no AccessoryLoader or something waiting for your response.

n0rt0nthec4t commented 5 years ago

... so the HAP-NodeJS accessory loader exits before get the chance to create the accessories ...

I assume you are using the Core.ts/BridgedCore.ts and placing your file into the accessories folder?

Yep, thats correct.. Its also running multiple accessories.. Was trying to avoid hard-coding device IPs into the source, hence doing auto-discovery sigh

NorthernMan54 commented 5 years ago

I have done this in homebridge, using dynamic plugins

https://github.com/NorthernMan54/homebridge-mculed

Device discovery using mdns etc

n0rt0nthec4t commented 5 years ago

Thanks, dont want to switch the a home bridge code base. Might have to see there is another way to modify the accessory loader code to perhaps allow and init function in the _accessory.js code its loading.

NorthernMan54 commented 5 years ago

Homebridge is just a wrapper around hap-nodejs, so it is feasible. Might be a place to start

n0rt0nthec4t commented 5 years ago

It seems the coding is different though, and I dont want a bridge to host the accessories

Supereg commented 5 years ago

... so the HAP-NodeJS accessory loader exits before get the chance to create the accessories ...

I assume you are using the Core.ts/BridgedCore.ts and placing your file into the accessories folder?

Yep, thats correct.. Its also running multiple accessories.. Was trying to avoid hard-coding device IPs into the source, hence doing auto-discovery sigh

Yes simply restructure you project to include hap-nodejs as dependency and publish accessories at any time. Here is a basic example how it looks like, though not the best or most detailed one (just picked some random plugin). Compare that with the source of Core.js and pick the lines you need.

stale[bot] commented 4 years ago

This issue has been automatically marked as stale because it has not had recent activity, and will be closed if no further activity occurs. If this issue was overlooked, forgotten, or should remain open for any other reason, please reply here to call attention to it and remove the stale status. Thank you for your contributions.