JakeBednard / node-phea

An unoffcial Phillips Hue Entertainment API library for Node.js
MIT License
32 stars 8 forks source link

Using this in a nodeAPI #6

Open pipje2001 opened 4 years ago

pipje2001 commented 4 years ago

I am trying to get this module to work in a nodeAPI, so i can use the hue entertainment api in python via this api of mine, now i am kinda running into a lot of problems, mainly that the lights don't change color or do anything at all:

i have this connect function:

router.get('/connect', async (req, res) => {
  try {
    let options = {
      address: '192.168.1.233',
      username: 'oyCbaYTEWX9QJ1X4UJNp4LZIDPa0lA6TPuwXhzc9',
      psk: 'B1E654E9E1BD7D761CCD6841D590B94E'
    };
    running = true;
    bridge = await Phea.bridge(options);
    console.log(bridge);
    res.send('Connected');
  } catch (err) {
    console.error(err.message);
    res.status(500).send('Server Error');
  }
});

which works fine, i removed the let before the bridge = await Phea.bridge(options);, so it can be used in different fucntions

i got a start and a stop function, stop just runs brige.stop and stops the loop which is running in basicExample (your example):

async function basicExample() {
  let groupId = 5;
  let transitionTime = 1000; // milliseconds
  await bridge.start(groupId);

  while (running) {
    let color = [
      // Generate Random RGB Color Array
      Math.floor(55 + Math.random() * Math.floor(200)),
      Math.floor(55 + Math.random() * Math.floor(200)),
      Math.floor(55 + Math.random() * Math.floor(200))
    ];

    // Set all lights to random color.
    bridge.transition(0, color, transitionTime);
    console.log(color);

    // Sleep until next color update is needed.
    await new Promise(resolve => setTimeout(resolve, transitionTime));
  }
}

so bassically it does nothing whith the lights at all, it does change the stream to active, but doesn't change any lights.

JakeBednard commented 4 years ago

Hi Pipje, looking into this now. I think the issue is related to removing the async, which leads to a race condition whereas the DTLS socket trys to connect before {"stream": "active"}. But additionally, it may be the way that the library is interacting with your server library threading/context.

I like the idea of hiding the library behind an HTTP API. It is a nice way to main a home server. What web framework are using (ExpressJS?). In your case, is the bridge being created on a global or local context? You'll need the async's for now, and potentially need to keep the bridge object in the global context. If you can provide a larger gist, I may be able to point you in the correct direction.

Regarding async, I'm trying to reconcile with myself if this is a JS anti-pattern. Give me sometime tow/ think about this, but startup procedure code wise needs to be sequential (eventually safety can be built in):

  1. Create bridge w/ Options dictionary object. Wait for return.
  2. Send HTTP request to HTTP:80 requesting DTLS group enable. Wait for return.
  3. Initialize DTLS Socket. (We need to this to block, but the code isn't fully there yet). 3a. Launch DTLS messaging to keep socket alive.
  4. Start streaming bridge.transition color changes.
  5. When done, close DTLS connection. (Socket + HTTP).
JakeBednard commented 4 years ago

Another fun idea with this one: I wonder if this library can somehow be interfaced through WASM and imported directly into Python. I'd love this as a (usually) Python first dev. In fact, the reason this library is written in Node is that Node's cross-platform support for DTLS sockets was better at the time. I'm not sure if python has improved in this area.

pipje2001 commented 4 years ago

I like the idea of hiding the library behind an HTTP API. It is a nice way to main a home server. What web framework are using (ExpressJS?). In your case, is the bridge being created on a global or local context? You'll need the async's for now, and potentially need to keep the bridge object in the global context. If you can provide a larger gist, I may be able to point you in the correct direction.

I am indeed using ExpressJS, the bridge is created as a global context otherwise the next time you send a request to the api it will not know what the bridge is. What do you mean with a larger gist?

pipje2001 commented 4 years ago

Another fun idea with this one: I wonder if this library can somehow be interfaced through WASM and imported directly into Python. I'd love this as a (usually) Python first dev. In fact, the reason this library is written in Node is that Node's cross-platform support for DTLS sockets was better at the time. I'm not sure if python has improved in this area.

Nope python has not improved in this erea, the whole reason i am doing this with a nodeAPI is because python doesn't support this type of dtls connection

JakeBednard commented 4 years ago

Hey pipje, I just updated the library. #7 was a similar bug to yours and I was able to pinpoint a bug causing lights not to change when their IDs were not sequential starting at "1". Give it a try and let me know if it resolves your issue.

JakeBednard commented 4 years ago

Just made issue #8 to add functionality somehow (this library or child library) for an HTTP server version of this.

pipje2001 commented 4 years ago

Hey pipje, I just updated the library. #7 was a similar bug to yours and I was able to pinpoint a bug causing lights not to change when their IDs were not sequential starting at "1". Give it a try and let me know if it resolves your issue.

Yep it fixed it so far!

Just made issue #8 to add functionality somehow (this library or child library) for an HTTP server version of this.

I will help with this if i have the time, but it should be to difficult

pipje2001 commented 4 years ago

Alright i tested the basicExample which worked but when i try to do it via a http request from python the server trows this error:

(node:14372) UnhandledPromiseRejectionWarning: TypeError: Cannot read property 'transitionColor' of undefined
    at PheaEngine.<anonymous> (b:\NAS CloudStation\PC\Prive\visual_studio_code\GitHub\WOT-Huenify\Hue Entertainment API\nodeAPI\node_modules\phea\build\phea-engine.js:58:50)
    at Generator.next (<anonymous>)
    at b:\NAS CloudStation\PC\Prive\visual_studio_code\GitHub\WOT-Huenify\Hue Entertainment API\nodeAPI\node_modules\phea\build\phea-engine.js:8:71
    at new Promise (<anonymous>)
    at __awaiter (b:\NAS CloudStation\PC\Prive\visual_studio_code\GitHub\WOT-Huenify\Hue Entertainment API\nodeAPI\node_modules\phea\build\phea-engine.js:4:12)
    at PheaEngine.transition (b:\NAS CloudStation\PC\Prive\visual_studio_code\GitHub\WOT-Huenify\Hue Entertainment API\nodeAPI\node_modules\phea\build\phea-engine.js:51:16)
    at lightId.forEach (b:\NAS CloudStation\PC\Prive\visual_studio_code\GitHub\WOT-Huenify\Hue Entertainment API\nodeAPI\node_modules\phea\build\hue-bridge.js:87:37)
    at Array.forEach (<anonymous>)
    at HueBridge.<anonymous> (b:\NAS CloudStation\PC\Prive\visual_studio_code\GitHub\WOT-Huenify\Hue Entertainment API\nodeAPI\node_modules\phea\build\hue-bridge.js:79:21)
    at Generator.next (<anonymous>)
(node:14372) UnhandledPromiseRejectionWarning: Unhandled promise rejection. This error originated either by throwing inside of an async function 
without a catch block, or by rejecting a promise which was not handled with .catch(). (rejection id: 1)
(node:14372) [DEP0018] DeprecationWarning: Unhandled promise rejections are deprecated. In the future, promise rejections that are not handled will terminate the Node.js process with a non-zero exit code.

I connect to the bridge with this function, which works fine i think:

// Connect to the bridge
router.post('/connect', async (req, res) => {
  try {
    const { hubIP, userName, psk } = req.body;
    let options = {
      address: hubIP,
      username: userName,
      psk: psk
    };
    console.log(`Connecting with bridge on ${hubIP}`);
    bridge = await Phea.bridge(options);

    console.log('Connected');
    res.send('Connected');
  } catch (err) {
    console.error(err.message);
    res.status(500).send('Server Error');
  }a

i start the stream with:

router.get('/start', async (req, res) => {
  try {
    const { groupID } = req.body;
    await bridge.start(groupID);
    running = true;
    console.log('Started');
    res.send('Started');
  } catch (err) {
    console.error(err.message);
    res.status(500).send('Server Error');
  }
});

and send a stream like this:

router.post('/stream', async (req, res) => {
  try {
    const { lightID = 0, RGB, transitionTime = 1000 } = req.body;
    console.log(lightID, RGB, transitionTime);
    bridge.transition(lightID, RGB, transitionTime);
    console.log('Streamed');
    res.send('Streamed');
  } catch (err) {
    console.error(err.message);
    res.status(500).send('Server Error');
  }
});

somehow this doesn't work and the example does....

JakeBednard commented 4 years ago

ACK. I'll see if I can get this working when I have a moment. I need to play with Express to get a feel for the namespacing. The code looks fine, so I imagine that the application context is getting misplaced or that theres a race. Here is where the error is originating from:


    constructor(id, options) {
        this.id = id;
        this.opts = options;
        this.rgb = [0, 0, 0];
        this.gen = null;
        this.transitionColor([0, 0, 0], 0);
    }
    transitionColor(rgb, tweenTime) {
        this.gen = this.setTransition(rgb, tweenTime);
    }
...```

So it sounds like the constructor is calls this.transitionColor just fine, but the secondary call during the stream exchange, the lights are undefined. I'd have to look, but as long as the stream is active from bridge.start(groupId), lights should be defined. Hmm if its not a context issue, I wonder if the bridge closes at some point. Two action items from this as well for enhancing the libraries debugging:

1) Create a configState message on bridge to dump a dictionary of bridge state and configuration.
2) If socket is not open, retry to open or throw.

I'll let you know when I am able to test this out.
pipje2001 commented 4 years ago

Just to be clear, it isn't nessecary to keep sending new messages to phea right?

JakeBednard commented 4 years ago

Yeah. PheaEngine schedules an interval to send a message every (1000/dtlsUpdatesPerSecond) milliseconds. Does express.js have any known issues with setInterval()? Nothing immediately shows up through my searching, but something keeps itching at me that we're dealing with a closed socket.

pipje2001 commented 4 years ago

I have no idea, i am not that experianced with express or js at all actually

pipje2001 commented 4 years ago

So i found the problem, i was sending a lightID of 5 which should be a light in the stream group, but there are only 2 lights in the group and it matched those lights to an id of 1 and 2, is this supposed to happen?

pipje2001 commented 4 years ago

This is awesome! finnaly after like half a year trying i can finnaly stream from a game for which i am making a mod. Thanks mate without this library and your help i would not have been able to do this.

pipje2001 commented 4 years ago

I do get these errors an awfull lot of times which breaks the nodeAPI:
throw err; ^

Error: Error: The DTLS handshake timed out

JakeBednard commented 4 years ago

No problem! Glad to see people get this library in action.

So regarding your light ID issue, were you able to get that worked out? Looking at the code, there's not a clear explanation for why this should be happening (assuming your using the latest build). The process is for building light id's is parsing the IDs from the bridge. The routine is:

  1. Query bridge for group configuration and state.
  2. Pass the the light Ids from the query response to a method to build light objects per light id.

The IDs used for these groups should come directly from the bridge. Not sure why they aren't matching.

For the DTLS timeout error occurs, I am familiar. I think there is an action item to currently rebuild the socket creation to attempt retrys and establish. Currently the creation is suffers from racing, which explains a bit of the behavior. Definitely a todo here.

JakeBednard commented 4 years ago

For the DTLS timeout errors, are you seeing them on bridge.start() or does this error occur well into runtime. I think that error is coming from the DTLS library I'm using.

chishankar commented 4 years ago

Hello Jake, thanks for the API :) I am your biggest fan

Durss commented 2 years ago

Hello and thanks a lot for your work !

I'm having the same DTLS handshake issue. Basically if my server is restarted i need to wait some time before the Bridge actually closes the connection and allows a new one to be opened (or at least that's my undesrtanding of it) The problem is that this package throws exceptions asynchronously which makes them uncatchable which stops the server's execution.

I made a fork that gets rid of those uncatchable errors if you wish : https://github.com/Durss/node-phea

I haven't tested it properly yet but i'll make a pull request later if you wish !

SebaGnich commented 2 years ago

Hey guys! :) Thanks for this lib, it is really great so far! I have the same issue like @pipje2001 with the transition Method. I'm using Typescript, and the documentation/definition doesn't fit (or maybe I don't understand it correctly).

Here you say you need a string or number (0 to change all): https://github.com/JakeBednard/node-phea/blob/master/src/phea-engine.ts#L70

But here you need an array of string or number: https://github.com/JakeBednard/node-phea/blob/master/build/hue-bridge.d.ts#L11

If I just ignore it and send 0, it works :)

Thanks again!