hap-rs icon indicating copy to clipboard operation
hap-rs copied to clipboard

Cannot pair with thermostat, ip_camera, light_sensor, or video_doorbell accessories

Open sbruton opened this issue 6 years ago • 7 comments

Pairing fails whenever a thermostat accessory is added.

To reproduce:

extern crate hap;

use hap::{
    accessory::{bridge, thermostat, Category, Information},
    transport::{IpTransport, Transport},
    Config,
};

fn main() {
    pretty_env_logger::init();
    let bridge = bridge::new(Information {
        name: "Bridge".into(),
        ..Default::default()
    })
    .unwrap();

    let thermostat = thermostat::new(Information {
        name: "Thermostat".into(),
        ..Default::default()
    })
    .unwrap();

    let config = Config {
        name: "Test".into(),
        category: Category::Bridge,
        ..Default::default()
    };

    let mut ip_transport = IpTransport::new(config).unwrap();
    ip_transport.add_accessory(bridge).unwrap();
    ip_transport.add_accessory(thermostat).unwrap();

    ip_transport.start().unwrap();
}

attempt to pair with code 11122333 fails with message "Home couldn't connect to this accessory". No errors are logged via the service.

changing the thermostat accessory to Outlet or Valve and pairing is successful.

sbruton avatar Oct 12 '18 20:10 sbruton

Looks like this behavior is also present with ip_camera, light_sensor, and video_doorbell. Uncommenting any of the add_accessory() lines below will result in failure to pair.

extern crate hap;

use hap::{
    accessory::{
        air_purifier, air_quality_sensor, bridge, carbon_dioxide_sensor, carbon_monoxide_sensor,
        contact_sensor, door, fan, fan_v2, garage_door_opener, heater_cooler,
        humidifier_dehumidifier, humidity_sensor, ip_camera, leak_sensor, light_sensor, lightbulb,
        lock, motion_sensor, occupancy_sensor, outlet, security_system, smoke_sensor,
        stateless_programmable_switch, switch, temperature_sensor, thermostat, valve,
        video_doorbell, window, window_covering, Category, Information,
    },
    transport::{IpTransport, Transport},
    Config, Error,
};

fn main() -> Result<(), Error> {
    let config = Config {
        name: "Test".into(),
        category: Category::Bridge,
        ..Default::default()
    };

    let mut ipt = IpTransport::new(config)?;

    ipt.add_accessory(air_purifier::new(Information::default())?)?;
    ipt.add_accessory(air_quality_sensor::new(Information::default())?)?;
    ipt.add_accessory(bridge::new(Information::default())?)?;
    ipt.add_accessory(carbon_dioxide_sensor::new(Information::default())?)?;
    ipt.add_accessory(carbon_monoxide_sensor::new(Information::default())?)?;
    ipt.add_accessory(contact_sensor::new(Information::default())?)?;
    ipt.add_accessory(door::new(Information::default())?)?;
    ipt.add_accessory(fan::new(Information::default())?)?;
    ipt.add_accessory(fan_v2::new(Information::default())?)?;
    ipt.add_accessory(garage_door_opener::new(Information::default())?)?;
    ipt.add_accessory(heater_cooler::new(Information::default())?)?;
    ipt.add_accessory(humidifier_dehumidifier::new(Information::default())?)?;
    ipt.add_accessory(humidity_sensor::new(Information::default())?)?;
    // ipt.add_accessory(ip_camera::new(Information::default())?)?;
    ipt.add_accessory(leak_sensor::new(Information::default())?)?;
    // ipt.add_accessory(light_sensor::new(Information::default())?)?;
    ipt.add_accessory(lightbulb::new(Information::default())?)?;
    ipt.add_accessory(lock::new(Information::default())?)?;
    ipt.add_accessory(motion_sensor::new(Information::default())?)?;
    ipt.add_accessory(occupancy_sensor::new(Information::default())?)?;
    ipt.add_accessory(outlet::new(Information::default())?)?;
    ipt.add_accessory(security_system::new(Information::default())?)?;
    ipt.add_accessory(smoke_sensor::new(Information::default())?)?;
    ipt.add_accessory(stateless_programmable_switch::new(Information::default())?)?;
    ipt.add_accessory(switch::new(Information::default())?)?;
    ipt.add_accessory(temperature_sensor::new(Information::default())?)?;
    // ipt.add_accessory(thermostat::new(Information::default())?)?;
    ipt.add_accessory(valve::new(Information::default())?)?;
    // ipt.add_accessory(video_doorbell::new(Information::default())?)?;
    ipt.add_accessory(window::new(Information::default())?)?;
    ipt.add_accessory(window_covering::new(Information::default())?)?;

    ipt.start()?;

    Ok(())
}

sbruton avatar Oct 12 '18 21:10 sbruton

So far I've found that the service is getting to the /pair-verify - M4: Sending Verify Finish Response where it sends back the following response for the accessory w/ thermostat service:

{
   "accessories":[
      {
         "aid":1,
         "services":[
            {
               "iid":1,
               "type":"3E",
               "hidden":false,
               "primary":false,
               "characteristics":[
                  {
                     "iid":2,
                     "type":"14",
                     "format":"bool",
                     "perms":[
                        "pw"
                     ]
                  },
                  {
                     "iid":3,
                     "type":"20",
                     "format":"string",
                     "perms":[
                        "pr"
                     ],
                     "value":"undefined"
                  },
                  {
                     "iid":4,
                     "type":"21",
                     "format":"string",
                     "perms":[
                        "pr"
                     ],
                     "value":"undefined"
                  },
                  {
                     "iid":5,
                     "type":"23",
                     "format":"string",
                     "perms":[
                        "pr"
                     ],
                     "value":"undefined"
                  },
                  {
                     "iid":6,
                     "type":"30",
                     "format":"string",
                     "perms":[
                        "pr"
                     ],
                     "value":"undefined",
                     "maxLen":64
                  },
                  {
                     "iid":7,
                     "type":"52",
                     "format":"string",
                     "perms":[
                        "pr"
                     ],
                     "value":"undefined"
                  }
               ]
            },
            {
               "iid":8,
               "type":"4A",
               "hidden":false,
               "primary":true,
               "characteristics":[
                  {
                     "iid":9,
                     "type":"F",
                     "format":"uint8",
                     "perms":[
                        "pr",
                        "ev"
                     ],
                     "value":0,
                     "valid-values":[
                        0,
                        1,
                        2
                     ]
                  },
                  {
                     "iid":10,
                     "type":"33",
                     "format":"uint8",
                     "perms":[
                        "pr",
                        "pw",
                        "ev"
                     ],
                     "value":0,
                     "valid-values":[
                        0,
                        1,
                        2,
                        3
                     ]
                  },
                  {
                     "iid":11,
                     "type":"11",
                     "format":"float",
                     "perms":[
                        "pr",
                        "ev"
                     ],
                     "value":0.0,
                     "unit":"celsius",
                     "maxValue":100.0,
                     "minStep":0.1
                  },
                  {
                     "iid":12,
                     "type":"35",
                     "format":"float",
                     "perms":[
                        "pr",
                        "pw",
                        "ev"
                     ],
                     "value":0.0,
                     "unit":"celsius",
                     "maxValue":38.0,
                     "minValue":10.0,
                     "minStep":0.1
                  },
                  {
                     "iid":13,
                     "type":"36",
                     "format":"uint8",
                     "perms":[
                        "pr",
                        "pw",
                        "ev"
                     ],
                     "value":0,
                     "valid-values":[
                        0,
                        1
                     ]
                  }
               ]
            }
         ]
      }
   ]
}

immediately thereafter an unpairing request comes through, whereas a working pairing next queries characteristics

sbruton avatar Oct 12 '18 23:10 sbruton

Thanks a lot for testing & reporting! The unpairing request seems to be the the expected behaviour of an iOS controller getting an invalid accessories JSON, e.g. with required characteristics missing or forbidden characteristics included in an accessory. I will look into this.

ewilken avatar Oct 14 '18 14:10 ewilken

So this is a weird one.

I discovered a bug causing zeroes as characteristic min_values not to be rendered into the auto-generated characteristics, but fixing it didn't change anything about the thermostat and light sensor accessories being invalid. The invalidity of the IP camera and video doorbell is obvious, as video streams aren't implemented in this crate yet.

Currently, I'm generating the accessories, services and characteristics from the HomeKit Accessory Simulator from the Additional Tools for Xcode 10.1 Beta 3.

I will publish the fix for the min_values as soon as the upcoming version of handlebars-rs it depends on is published and keep investigating the invalidity of the two accessories, but unfortunately I don't have much time for it at the moment.

Thanks for your patience and feel free to contribute if you find out anything about this issue in the meantime.

ewilken avatar Oct 21 '18 14:10 ewilken

@ewilken Hey i was curious sine the above is over a year old, do you have video streams working yet? I see the use of CameraRTPStreamManagement but it's auto generated type. Just cant tell.

nusairat avatar Feb 23 '20 07:02 nusairat

In the example above, it at least looks like IID 12 should have a minimum value of 10.0 but has a value of 0.0 That doesn't look right

soundprojects avatar Aug 13 '22 16:08 soundprojects

Perhaps we should compare values with the values that the simulator sets on a 'fresh' set up of these accessories and that the default values should mimic them to an extent that they seem to have some influence on the pairing process?

soundprojects avatar Aug 13 '22 16:08 soundprojects