hap-java / HAP-Java

Java implementation of the HomeKit Accessory Protocol
MIT License
153 stars 82 forks source link

Unable to add accessory, EVE stuck saying 'Adding Accessory' #21

Closed Shaun-Sheppard closed 2 years ago

Shaun-Sheppard commented 7 years ago

I'm trying to setup a standalone accessory using the default MockSwitch accessory and the default MockAuthInfo class.

I open EVE on my iPhone, I'm able to see the accessory, select it, enter the access and then EVE gets stuck showing 'Adding Device'. It's as if the HAP server isn't letting EVE know that the process is complete.

Here's the console output from trying to add the accessory;

20:06:47.796 [defaultEventExecutorGroup-4-1] INFO com.beowulfe.hap.impl.http.impl.AccessoryHandler - New homekit connection from /192.168.0.12:49431 20:06:47.799 [nioEventLoopGroup-3-1] DEBUG io.netty.util.Recycler - -Dio.netty.recycler.maxCapacity.default: 262144 20:06:47.813 [defaultEventExecutorGroup-4-1] INFO com.beowulfe.hap.impl.pairing.PairingManager - Starting pair for Test Lightbulb 2 20:06:47.945 [defaultEventExecutorGroup-4-1] INFO com.beowulfe.hap.impl.http.HomekitClientConnection - 200 /pair-setup 20:06:47.949 [defaultEventExecutorGroup-4-1] DEBUG io.netty.util.internal.Cleaner0 - java.nio.ByteBuffer.cleaner(): available 20:06:53.227 [defaultEventExecutorGroup-4-1] DEBUG com.beowulfe.hap.impl.pairing.PairingManager - Entering second stage of pair for Test Lightbulb 2 20:06:53.326 [defaultEventExecutorGroup-4-1] INFO com.beowulfe.hap.impl.http.HomekitClientConnection - 200 /pair-setup 20:06:53.432 [defaultEventExecutorGroup-4-1] DEBUG com.beowulfe.hap.impl.pairing.PairingManager - Entering third stage of pair for Test Lightbulb 2 Added pairing for 6:a8:6c:d7:ca:ca891C4817-03C3-4F7B-9531-417AB50C92D2 20:06:53.552 [defaultEventExecutorGroup-4-1] INFO com.beowulfe.hap.impl.jmdns.JmdnsHomekitAdvertiser - Re-creating service due to change in discoverability to false 20:06:55.559 [defaultEventExecutorGroup-4-1] INFO com.beowulfe.hap.impl.jmdns.JmdnsHomekitAdvertiser - Registering _hap._tcp.local. on port 9124 20:06:58.151 [defaultEventExecutorGroup-4-1] INFO com.beowulfe.hap.impl.http.HomekitClientConnection - 200 /pair-setup 20:06:58.370 [defaultEventExecutorGroup-4-1] INFO com.beowulfe.hap.impl.http.impl.AccessoryHandler - Terminated homekit connection from /192.168.0.12:49431 20:06:58.380 [nioEventLoopGroup-2-1] INFO io.netty.handler.logging.LoggingHandler - [id: 0x1164b103, /0:0:0:0:0:0:0:0:9124] RECEIVED: [id: 0x4d11f81b, /192.168.0.12:49432 => /192.168.0.14:9124] 20:06:58.381 [defaultEventExecutorGroup-4-2] INFO com.beowulfe.hap.impl.http.impl.AccessoryHandler - New homekit connection from /192.168.0.12:49432 20:06:58.384 [defaultEventExecutorGroup-4-2] DEBUG com.beowulfe.hap.impl.pairing.PairVerificationManager - Starting pair verification for Test Lightbulb 2 20:06:58.391 [defaultEventExecutorGroup-4-2] INFO com.beowulfe.hap.impl.http.HomekitClientConnection - 200 /pair-verify 20:06:58.453 [defaultEventExecutorGroup-4-2] DEBUG com.beowulfe.hap.impl.pairing.PairVerificationManager - Completed pair verification for Test Lightbulb 2 20:06:58.454 [defaultEventExecutorGroup-4-2] INFO com.beowulfe.hap.impl.http.HomekitClientConnection - 200 /pair-verify 20:06:58.458 [defaultEventExecutorGroup-4-2] DEBUG io.netty.util.internal.JavassistTypeParameterMatcherGenerator - Generated: io.netty.util.internal.__matchers__.io.netty.buffer.ByteBufMatcher 20:06:58.492 [defaultEventExecutorGroup-4-2] INFO com.beowulfe.hap.impl.http.HomekitClientConnection - 200 /accessories 20:06:58.632 [defaultEventExecutorGroup-4-2] INFO com.beowulfe.hap.impl.connections.SubscriptionManager - Added subscription to class com.beowulfe.hap.impl.characteristics.common.PowerStateCharacteristic for 1998817436 20:06:58.635 [defaultEventExecutorGroup-4-2] INFO com.beowulfe.hap.impl.http.HomekitClientConnection - 204 /characteristics 20:09:37.209 [defaultEventExecutorGroup-4-2] INFO com.beowulfe.hap.impl.connections.SubscriptionManager - Removed subscription to class com.beowulfe.hap.impl.characteristics.common.PowerStateCharacteristic for 1998817436 20:09:37.209 [defaultEventExecutorGroup-4-2] INFO com.beowulfe.hap.impl.http.HomekitClientConnection - 204 /characteristics

I've tried creating another Accessory on the server and exactly the same issue happens with this too.

I have had the same issue when adding accessories to a bridge. The bridge adds to my iPhone fine, but 9 times out of ten the individual accessories don't show on my phone.

Any help would be appreciated.

andylintner commented 7 years ago

It looks like you need to implement the HomekitAuthInfo.hasUser method. I need to update the sample - it's required as of iOS9. See http://beowulfe.github.io/HAP-Java/apidocs/com/beowulfe/hap/HomekitAuthInfo.html#hasUser--

Shaun-Sheppard commented 7 years ago

Thanks for the response. In my own Auth file I do have a getUser method which uses a List of users persisted in a JSON file, and that doesn't seem to work either.

`public class MockAuthInfo implements HomekitAuthInfo {

private static final String PIN = "031-45-154";

private List<HAPUser> users = new ArrayList<>();
private String mac;
private BigInteger salt;
private byte[] privateKey;
private final ConcurrentMap<String, byte[]> userKeyMap = new ConcurrentHashMap<>();

public MockAuthInfo() throws InvalidAlgorithmParameterException {
    mac = HomekitServer.generateMac();
    salt = HomekitServer.generateSalt();
    privateKey = HomekitServer.generateKey();
    System.out.println("The PIN for pairing is "+PIN);

    restorePersistence();

    System.out.println("Private key = " + new String(privateKey));
    System.out.println("Mac = " + mac);
    System.out.println("Salt = " + salt.toString());
}

private void restorePersistence() {
    JSONParser parser = new JSONParser();

    /* Clear out any existing users */
    users.clear();

    try {
        Object obj = parser.parse(new FileReader("/tmp/hap_config"));

        JSONObject jsonObject = (JSONObject) obj;

        this.mac = (String) jsonObject.get("mac");

        String jSalt = (String) jsonObject.get("salt");
        this.salt = new BigInteger((String) jsonObject.get("salt"));

        String jPrivateKey = (String) jsonObject.get("privateKey");
        this.privateKey = Base64.decodeBase64(jPrivateKey);

        JSONArray jsonUsers = (JSONArray) jsonObject.get("users");
        for (Object userObject : jsonUsers) {
            JSONObject jUser = (JSONObject) userObject;

            String uName = (String) jUser.get("username");
            String uPublicKey = (String) jUser.get("publicKey");

            System.out.println("Restored username " + uName + " key " + Base64.decodeBase64(uPublicKey));

            HAPUser user = new HAPUser();
            user.setUsername(uName);
            user.setPublicKey(Base64.decodeBase64(uPublicKey));
            users.add(user);
        }
    } catch (FileNotFoundException e) {
        System.out.println("HAP Users file not found... creating");
        persist();
    } catch (Exception e) {
        System.out.println("HAP Users file not found... creating");
        e.printStackTrace();
    }

    for (HAPUser user : users) {
        System.out.println("Username " + user.getUsername() + " restored.");
    }
}

private void persist() {
    System.out.println("Persisting data...");

    JSONObject obj = new JSONObject();
    JSONArray usrs = new JSONArray();

    for (HAPUser user : users) {
        JSONObject u = new JSONObject();
        u.put("username", user.getUsername());
        u.put("publicKey", Base64.encodeBase64String(user.getPublicKey()));
        usrs.add(u);
    }

    obj.put("mac", mac);
    obj.put("privateKey", Base64.encodeBase64String(privateKey));
    obj.put("salt", salt.toString());

    obj.put("users", usrs);

    try {
        FileWriter file = new FileWriter("/tmp/hap_config");
        file.write(obj.toJSONString());
        file.flush();
        file.close();
    } catch (IOException e) {
        e.printStackTrace();
    }
}

@Override
public String getPin() {
    return PIN;
}

@Override
public String getMac() {
    return mac;
}

@Override
public BigInteger getSalt() {
    return salt;
}

@Override
public byte[] getPrivateKey() {
    return privateKey;
}

@Override
public void createUser(String username, byte[] publicKey) {
    /* Remove the user first, just incase it already exists */
    removeUser(username);

    HAPUser user = new HAPUser();
    user.setUsername(username);
    user.setPublicKey(publicKey);
    users.add(user);

    persist();

    System.out.println("Added pairing for "+username+ " key " + Base64.encodeBase64String(publicKey));
}

@Override
public void removeUser(String username) {
    int index = -1;

    for (HAPUser user : users) {
        if (user.getUsername().equals(username)) {
            index = users.indexOf(user);
        }
    }

    if (index > -1) {
        users.remove(index);
    }

    persist();

    System.out.println("Removed pairing for "+username);
}

@Override
public byte[] getUserPublicKey(String username) {
    for (HAPUser user : users) {
        if (user.getUsername().equals(username)) {
            return user.getPublicKey();
        }
    }

    return null;
}

@Override
public boolean hasUser() {
    if (users.size() > 0) {
        System.out.println("Has users? yes.");
        return true;
    } else {
        System.out.println("Has users? no.");
        return false;
    }
};

}`

andylintner commented 7 years ago

Issues like this are usually attributable to something wrong in the mdns record HomeKit uses to advertise itself. You might try firing up an mdns browser and look at the data. You can post it here and I can take a look too.

On Sunday, September 11, 2016, shepparddigital notifications@github.com wrote:

Thanks for the response. In my own Auth file I do have a getUser method which uses a List of users persisted in a JSON file, and that doesn't seem to work either.

`public class MockAuthInfo implements HomekitAuthInfo {

private static final String PIN = "031-45-154";

private List users = new ArrayList<>(); private String mac; private BigInteger salt; private byte[] privateKey; private final ConcurrentMap<String, byte[]> userKeyMap = new ConcurrentHashMap<>();

public MockAuthInfo() throws InvalidAlgorithmParameterException { mac = HomekitServer.generateMac(); salt = HomekitServer.generateSalt(); privateKey = HomekitServer.generateKey(); System.out.println("The PIN for pairing is "+PIN);

restorePersistence();

System.out.println("Private key = " + new String(privateKey));
System.out.println("Mac = " + mac);
System.out.println("Salt = " + salt.toString());

}

private void restorePersistence() { JSONParser parser = new JSONParser();

/* Clear out any existing users */
users.clear();

try {
    Object obj = parser.parse(new FileReader("/tmp/hap_config"));

    JSONObject jsonObject = (JSONObject) obj;

    this.mac = (String) jsonObject.get("mac");

    String jSalt = (String) jsonObject.get("salt");
    this.salt = new BigInteger((String) jsonObject.get("salt"));

    String jPrivateKey = (String) jsonObject.get("privateKey");
    this.privateKey = Base64.decodeBase64(jPrivateKey);

    JSONArray jsonUsers = (JSONArray) jsonObject.get("users");
    for (Object userObject : jsonUsers) {
        JSONObject jUser = (JSONObject) userObject;

        String uName = (String) jUser.get("username");
        String uPublicKey = (String) jUser.get("publicKey");

        System.out.println("Restored username " + uName + " key " + Base64.decodeBase64(uPublicKey));

        HAPUser user = new HAPUser();
        user.setUsername(uName);
        user.setPublicKey(Base64.decodeBase64(uPublicKey));
        users.add(user);
    }
} catch (FileNotFoundException e) {
    System.out.println("HAP Users file not found... creating");
    persist();
} catch (Exception e) {
    System.out.println("HAP Users file not found... creating");
    e.printStackTrace();
}

for (HAPUser user : users) {
    System.out.println("Username " + user.getUsername() + " restored.");
}

}

private void persist() { System.out.println("Persisting data...");

JSONObject obj = new JSONObject();
JSONArray usrs = new JSONArray();

for (HAPUser user : users) {
    JSONObject u = new JSONObject();
    u.put("username", user.getUsername());
    u.put("publicKey", Base64.encodeBase64String(user.getPublicKey()));
    usrs.add(u);
}

obj.put("mac", mac);
obj.put("privateKey", Base64.encodeBase64String(privateKey));
obj.put("salt", salt.toString());

obj.put("users", usrs);

try {
    FileWriter file = new FileWriter("/tmp/hap_config");
    file.write(obj.toJSONString());
    file.flush();
    file.close();
} catch (IOException e) {
    e.printStackTrace();
}

}

@Override public String getPin() { return PIN; }

@Override public String getMac() { return mac; }

@Override public BigInteger getSalt() { return salt; }

@Override public byte[] getPrivateKey() { return privateKey; }

@Override public void createUser(String username, byte[] publicKey) { /* Remove the user first, just incase it already exists */ removeUser(username);

HAPUser user = new HAPUser();
user.setUsername(username);
user.setPublicKey(publicKey);
users.add(user);

persist();

System.out.println("Added pairing for "+username+ " key " + Base64.encodeBase64String(publicKey));

}

@Override public void removeUser(String username) { int index = -1;

for (HAPUser user : users) {
    if (user.getUsername().equals(username)) {
        index = users.indexOf(user);
    }
}

if (index > -1) {
    users.remove(index);
}

persist();

System.out.println("Removed pairing for "+username);

}

@Override public byte[] getUserPublicKey(String username) { for (HAPUser user : users) { if (user.getUsername().equals(username)) { return user.getPublicKey(); } }

return null;

}

@Override public boolean hasUser() { if (users.size() > 0) { System.out.println("Has users? yes."); return true; } else { System.out.println("Has users? no."); return false; } };

}`

— You are receiving this because you commented. Reply to this email directly, view it on GitHub https://github.com/beowulfe/HAP-Java/issues/21#issuecomment-246199642, or mute the thread https://github.com/notifications/unsubscribe-auth/AAEwwNCwxYdorbdntrSWe2AydPV-a4fSks5qpFh5gaJpZM4J6FDz .

Shaun-Sheppard commented 7 years ago

Here's a screenshot of what shows in an mdns browser, I'm not really sure what I'm looking for, but I did notice that before trying to add the accessory the data looked like this...

mdns

After adding and getting stuck saying 'Adding accessory' it changed to this.

mdns2

andylintner commented 7 years ago

That all looks perfect. Must be something in the http response instead. Try turning the logging level up to TRACE - it will capture the http requests and responses.

On Monday, September 12, 2016, shepparddigital notifications@github.com wrote:

Here's a screenshot of what shows in an mdns browser, I'm not really sure what I'm looking for, but I did notice that before trying to add the accessory the data looked like this...

[image: mdns] https://cloud.githubusercontent.com/assets/5153221/18427852/9ab6815a-78c2-11e6-95b8-4ee25da7552e.jpg

After adding and getting stuck saying 'Adding accessory' it changed to this.

[image: mdns2] https://cloud.githubusercontent.com/assets/5153221/18427877/acbbc8f6-78c2-11e6-862f-ee55bc92e8b2.jpg

— You are receiving this because you commented. Reply to this email directly, view it on GitHub https://github.com/beowulfe/HAP-Java/issues/21#issuecomment-246268796, or mute the thread https://github.com/notifications/unsubscribe-auth/AAEwwATsOGagDXJfCTd3ROHDcbDqxUITks5qpP7GgaJpZM4J6FDz .

Shaun-Sheppard commented 7 years ago

Sorry, I seem to be having issues changing the log level. I've added -Dorg.slf4j.simpleLogger.defaultLogLevel=TRACE to the VM arguments but I don't see any additional information?

Shaun-Sheppard commented 7 years ago

I figured out how to change the log level to trace, I'm attaching the trace as a txt file as it's a little too long to paste in here I think.

trace.txt

Shaun-Sheppard commented 7 years ago

Is there anything else I can provide to help diagnose this issue?

mariusz2108 commented 7 years ago

I had similar issue on my iPhone with iOS 10. When I deleted cache on iPhone (I used 3rd party app) everything worked fine.

comdata commented 7 years ago

Is there any new on this issue?

leonborko commented 7 years ago

Sample project is not working by me. It hangs by pairing. Can you prepare working sample or post link to working sample

bakkerv commented 7 years ago

I have the exact same issue with the pairing (iOS 10)

leonborko commented 7 years ago

I have solved problem with sample. My computer has multiple network interfaces and sample app get wrong ip address. Just change HomekitServer homekit = new HomekitServer(PORT); to HomekitServer homekit = new HomekitServer(NETWORK ADDRESS, PORT);

Network address in ip address of your computer

yfre commented 2 years ago

close as solution found according to the last comment