ebaauw / homebridge-lib

Utility Library for Homebridge Plugins
Apache License 2.0
94 stars 12 forks source link

Error: Cannot add a bridged Accessory with the same UUID as another bridged Accessory #2

Closed robin7331 closed 6 years ago

robin7331 commented 7 years ago

Hey there!

I have a certain problem which I cant solve. Within my HomePlatform.js I'm adding a new accessory. The problem is that I always get an uncaught exception for adding a bridged accessory with the same UUID as another bridged accessory.

I already deleted the cachedAccessories file. What am I doing wrong? Is this the wrong place (wrong way?) to add an accessory?

This is my HomePlatform.js

const HomeBridgeLib = require('homebridge-lib');
const LibPlatform = HomeBridgeLib.LibPlatform;

const LightBulb = require('./Accessories/LightBulb');

// Called by homebridge when initialising the plugin.
class HomePlatform extends LibPlatform {
  constructor(log, configJson, homebridge) {
    super(log, configJson, homebridge);

    let params = {
      name: 'MyLight1',
      id: 'myLight'
    }
    let myAccessory = new LightBulb(this, params);

  }
};

module.exports = HomePlatform;

And this is my LightBulb.js:

const HomeBridgeLib = require('homebridge-lib');
const LibAccessory = HomeBridgeLib.LibAccessory;

class LightBulb extends LibAccessory {
  constructor(platform, params) {
    super(platform, params);

  }
}

module.exports = LightBulb;

Thanks!

robin7331 commented 7 years ago

If I listen for the didFinishLaunching event within the HomePlatform constructor and only then add a new accessory everything runs fine.

 // Called by homebridge when initialising the plugin.
class HomePlatform extends LibPlatform {
  constructor(log, configJson, homebridge) {
    super(log, configJson, homebridge);

    this._homebridge.on('didFinishLaunching', () => {
      let myAccessory = new LightBulb(this); // generates a random ID based on the timestamp
    });

  }
};

Is that the normal way you would add a new accessory?

ebaauw commented 7 years ago

When using homebridge-lib, the idea is to create a subclass of LibAccessory for your accessories, passing a unique value for params.id in the constructor, which is used to generate a unique UUID, see https://github.com/ebaauw/homebridge-lib/wiki/LibAccessory#constructor.

homebridge-lib handles the didFinishLaunching stuff. You would create your accessories from the UPnP or heartbeat event handlers, not from the platform constructor, which only sets up these event handlers (see https://github.com/ebaauw/homebridge-hue/blob/v1.0/lib/HuePlatform.js#L198).

robin7331 commented 7 years ago

Well I created my LightBulb class which extends LibAccessory. So that part is correct I guess.

But what don't really get is why the appropriate way to add an accessory to the platform is within the heartbeat. Am I correct that you implemented the heartbeat to be used to for example discover new devices. And if so add them to the platform.

But I don't really have that kind of scenario. It's more that I'd like to restart the homebridge if I add devices. So I basically need to add missing (or new) accessories only once when that thing starts.

If I get you correctly the most appropriate way here with your library would be to use the heartbeat only once to add accessories and then simply ignore it?

Also why does the code even crash when I add an accessory in the platform constructor? Seems like if it adds it twice or something.

ebaauw commented 7 years ago

It's more that I'd like to restart the homebridge if I add devices. So I basically need to add missing (or new) accessories only once when that thing starts.

One of the ideas behind using dynamic platform accessories, is that you no longer need to restart homebridge when adding new devices - the corresponding accessories can be added at run-time.

How do you detect the new devices? For now, homebridge-lib supports two methods for this: UPnP discovery or periodic polling (through the heartbeat). If the devices your platform supports offer other types of event notifications, you'd setup a handler for these in the platform constructor, and create the accessory from this handler. I'll need do this for my Sonos plugin (once I've got the Hue plugin fully refactored to use homebridge-lib), but I haven't figured out a way to generalise this in homebridge-lib.

If I get you correctly the most appropriate way here with your library would be to use the heartbeat only once to add accessories and then simply ignore it?

Could do. Check for beat === 0 in your heartbeat handler. Or check for new devices once every hour (by checking for beat % 3600 === 0).

Also why does the code even crash when I add an accessory in the platform constructor?

The crash is courtesy of homebridge, which checks that the UUID of the new platformAccessory instance is unique. homebridge-lib generates the UUID from the params.id value (https://github.com/ebaauw/homebridge-lib/blob/master/lib/LibAccessory.js#L37). In your first example (constructor) you use a hard-coded value for this: 'myLight'. In your second example, the comment states it's a randomly generated id. I assume you set params.id in your LightBulb constructor before calling super(platform, params)? I think that difference might be causing/preventing the crash?

Both examples are bad practice. You want id to be stable, based on the device's serial number or mac address, so that when you're forced to reset the homebridge config (e.g. deleting the cached accessories), HomeKit still recognises the same accessories (or you'd lose HomeKit room assignments, scenes, automations).

Seems like if it adds it twice or something.

Note that homebridge re-creates the platformAccessory instance from cache, if it was created in an earlier session. You need a handler for accessoryRestored (see https://github.com/ebaauw/homebridge-hue/blob/v1.0/lib/HuePlatform.js#L222) to re-create a LightBulb instance, which attaches to the restored platformAccessory instance, based on id.

robin7331 commented 7 years ago

I'm new to the homebridge (and HomeKit) universe. Thats why I really appreciate your time and advice!

One of the ideas behind using dynamic platform accessories, is that you no longer need to restart > homebridge when adding new devices - the corresponding accessories can be added at run-time. How do you detect the new devices?

For now I did not plan to automatically discover devices. I'd prefer a config file describing all the individual devices and their configurations. If I add a new physical device to my home my plan is to simply update my configuration. I will have a mixture of devices with all sorts of interfaces ranging from wifi over serial to virtual devices.

The crash is courtesy of homebridge, which checks that the UUID of the new platformAccessory instance is unique. homebridge-lib generates the UUID from the params.id value

'lightBulb' will not be my final ID. Fully agree that this should be the serial number or some other unique and fixed value. I'm currently just tinkering around. If I hard code the ID to 'lightBulb' but add it 10 times it still should not cause any trouble because you first check if the device already exists prior to its creation, right? https://github.com/ebaauw/homebridge-lib/blob/61297d69bc11652606eb9eab5e70fd87cc2f1ada/lib/LibAccessory.js#L34

In your first example (constructor) you use a hard-coded value for this: 'myLight'. In your second example, the comment states it's a randomly generated id. I assume you set params.id in your LightBulb constructor before calling super(platform, params)? I think that difference might be causing/preventing the crash?

If I randomly generate an ID or hardcode it. The crash stays the same. I even removed all cached files from my ~/.homebridge folder before executing the code. I can only fix that by using the heartbeat method to add my accessory or by listening for the finished event and then add it.

ebaauw commented 7 years ago

If I randomly generate an ID or hardcode it. The crash stays the same. I even removed all cached files from my ~/.homebridge folder before executing the code. I can only fix that by using the heartbeat method to add my accessory or by listening for the finished event and then add it.

Odd. Could you please post the output of homebridge here (or preferably of homebridge -D), incl. the stack trace of the crash?

robin7331 commented 7 years ago

Sure. So if this is my code it runs perfectly fine:

class HomePlatform extends LibPlatform {
  constructor(log, configJson, homebridge) {
    super(log, configJson, homebridge);

    this.on('heartbeat', (beat) => {

      if (beat === 0) {
        new LightBulb(this, {
          name: "TestLight",
          id: "12345"
        });
      }

    });

  }

But if I change it to this it crashes:


class HomePlatform extends LibPlatform {
  constructor(log, configJson, homebridge) {
    super(log, configJson, homebridge);

    new LightBulb(this, {
      name: "TestLight",
      id: "12345"
    });

  }

Console output for DEBUG=* homebridge -D -U ~/.homebridge-dev -P ./

[9/22/2017, 12:37:20 PM] Loaded plugin: home-platform
[9/22/2017, 12:37:20 PM] Registering platform 'homebridge-HomePlatform.HomePlatform'
[9/22/2017, 12:37:20 PM] ---
[9/22/2017, 12:37:20 PM] Loaded plugin: homebridge-server
[9/22/2017, 12:37:20 PM] Registering platform 'homebridge-server.Server'
[9/22/2017, 12:37:20 PM] ---
[9/22/2017, 12:37:20 PM] Loaded config.json with 0 accessories and 1 platforms.
[9/22/2017, 12:37:20 PM] ---
[9/22/2017, 12:37:20 PM] Loading 1 platforms...
[9/22/2017, 12:37:20 PM] [HomePlatform] Initializing HomePlatform platform...
[9/22/2017, 12:37:20 PM] [HomePlatform] TestLight: set Name to TestLight
[9/22/2017, 12:37:20 PM] [HomePlatform] TestLight: set Serial Number to 12345
  Accessory Reachability update is no longer being supported. +0ms
[9/22/2017, 12:37:20 PM] [HomePlatform] My Light: set Name to My Light
[9/22/2017, 12:37:20 PM] [HomePlatform] My Light: set Hue to 0
[9/22/2017, 12:37:20 PM] Loading 0 accessories...
[9/22/2017, 12:37:20 PM] [HomePlatform] TestLight: {"className":"LightBulb","name":"TestLight","id":"12345","Lightbulb":{"name":"My Light","hue":0}}
[9/22/2017, 12:37:20 PM] [HomePlatform] uncaught exception:  Error: Cannot add a bridged Accessory with the same UUID as another bridged Accessory: 8CB2237D-0679-4CA8-8DB6-464EAC60DA96
    at Bridge.Accessory.addBridgedAccessory (/usr/local/lib/node_modules/homebridge/node_modules/hap-nodejs/lib/Accessory.js:257:13)
    at Server._configCachedPlatformAccessories (/usr/local/lib/node_modules/homebridge/lib/server.js:348:18)
    at Server.run (/usr/local/lib/node_modules/homebridge/lib/server.js:83:8)
    at module.exports (/usr/local/lib/node_modules/homebridge/lib/cli.js:40:10)
    at Object.<anonymous> (/usr/local/lib/node_modules/homebridge/bin/homebridge:17:22)
    at Module._compile (module.js:573:32)
    at Object.Module._extensions..js (module.js:582:10)
    at Module.load (module.js:490:32)
    at tryModuleLoad (module.js:449:12)
    at Function.Module._load (module.js:441:3)
[9/22/2017, 12:37:20 PM] [HomePlatform] homebridge exiting with status 0

The error occurs even if I delete the cached files.

ebaauw commented 7 years ago

This issue has nothing to do with homebridge-lib - it's how homebridge works:

Note that I've hidden the whole create/register platformAccessory logic in the constructor of LibAccessory. Creating a platformAccessory from the platform constructor would be OK, but you can only register it from the didFinishLaunching callback.

Note that homebridge doesn't crash. homebridge-lib actually catches the exception, preventing a crash. However as the exception prevents homebridge from starting its http server (for HomeKit to connect to), NodeJS simply exists because there's nothing left to do.

johnsills1 commented 5 years ago

I’m having a similar problem installing Chamberlain accessory. I get the duplicate UUID error. I’m a novice at Homebridge. Any help appreciated