Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

IOS Home App not showing accessory details #75

Open
soundprojects opened this issue Aug 12, 2022 · 2 comments
Open

IOS Home App not showing accessory details #75

soundprojects opened this issue Aug 12, 2022 · 2 comments

Comments

@soundprojects
Copy link
Contributor

When adding accessories to the Ios Home app, pressing the accessory with force or touch or clicking accessory details does not make anything happen. It is like the app is experiencing some error. It only happens with the services I add with hap-rs

For example here is my custom irrigation system code:

use hap::{
    accessory::{HapAccessory, AccessoryInformation},
    service::{
        accessory_information::AccessoryInformationService,
        humidity_sensor::HumiditySensorService,
        temperature_sensor::TemperatureSensorService,
        HapService, valve::ValveService,
    },
    HapType, characteristic::HapCharacteristic,
};

use serde::{
    ser::{SerializeStruct, Serializer},
    Serialize,
};
use serde_json::{Value, Number};

/// Multi Sensor accessory.
#[derive(Debug, Default)]
pub struct HydroponicAccessory {
    /// ID of the Multi Sensor accessory.
    id: u64,

    /// Accessory Information service.
    pub accessory_information: AccessoryInformationService,
    /// Temperature Sensor service.
    pub temperature_sensor: TemperatureSensorService,
    /// TDS Sensor service.
    pub tds_sensor: HumiditySensorService,
    /// Valve service
    pub valve: ValveService
}

impl HydroponicAccessory{
    pub fn new(id: u64, acc_info: AccessoryInformation) -> Self{

        let mut hydroponic = HydroponicAccessory {
            id,
            accessory_information: acc_info.to_service(1,1).expect("Failed to create service"),
            // accessory information service ends at IID 6, so we start counting at 7
            temperature_sensor: TemperatureSensorService::new(7, 1),
            // teperature sensor service ends at IID 13, so we start counting at 14
            tds_sensor: HumiditySensorService::new(14, 1),
            // humidity sensor service ends at IID 20, so we start counting at 21
            valve: ValveService::new(21, 1)
        };

        hydroponic.valve.set_primary(true);
        hydroponic.valve.active.set_value(Value::Number(Number::from(1)));
        
        if let Some(ref mut act)  = hydroponic.temperature_sensor.status_active{
            act.set_value(Value::Number(Number::from(1)));
        }

        if let Some(ref mut act)  = hydroponic.tds_sensor.status_active{
            act.set_value(Value::Number(Number::from(1)));
        }

        hydroponic
    }
}

impl HapAccessory for HydroponicAccessory {

    fn get_id(&self) -> u64 { self.id }

    fn set_id(&mut self, id: u64) { self.id = id; }

    fn get_service(&self, hap_type: HapType) -> Option<&dyn HapService> {
        for service in self.get_services() {
            if service.get_type() == hap_type {
                return Some(service);
            }
        }
        None
    }

    fn get_mut_service(&mut self, hap_type: HapType) -> Option<&mut dyn HapService> {
        for service in self.get_mut_services() {
            if service.get_type() == hap_type {
                return Some(service);
            }
        }
        None
    }

    fn get_services(&self) -> Vec<&dyn HapService> {
        vec![
            &self.accessory_information,
            &self.temperature_sensor,
            &self.tds_sensor,
            &self.valve
        ]
    }

    fn get_mut_services(&mut self) -> Vec<&mut dyn HapService> {
        vec![
            &mut self.accessory_information,
            &mut self.temperature_sensor,
            &mut self.tds_sensor,
            &mut self.valve
        ]
    }
}

impl Serialize for HydroponicAccessory {
    fn serialize<S: Serializer>(&self, serializer: S) -> std::result::Result<S::Ok, S::Error> {
        let mut state = serializer.serialize_struct("HapAccessory", 2)?;
        state.serialize_field("aid", &self.get_id())?;
        state.serialize_field("services", &self.get_services())?;
        state.end()
    }
}

It connects properly but the valve is absent from the configuration and pressing the accessory to show its details does not do anything at all.

What would be a good starting point to look into this? The dnssd seems to work properly. Debug shows that TCP streams are setup. How can you verify that what the service needs to send as configuration is what the ios app is expecting?

Thanks

@soundprojects
Copy link
Contributor Author

soundprojects commented Aug 12, 2022

So with some help of the Homekit Accessory Simulator, I figured out what was missing

This might be useful to put in the documentation:

In the example above, the IOS home app expects the 'status_active' of the temperature and humidity sensor to be set to true and most importantly, it expects the 'is_configured' value of the valve to be set to true as well

The Active and In Use values characteristics then indicate whether the Valve is Active and (if either manually or some automatic program you develop is running) you set the In Use to true. These two values set to true make the switch visible in the 'on' state and the labels set to 'on'

When this is set using the following code:

    if let Some(ref mut conf) = hydro.valve.is_configured{
        conf.set_value(Value::Number(Number::from(1))).await.unwrap();
    }

    if let Some(ref mut conf) = hydro.temperature_sensor.status_active{
        conf.set_value(Value::Number(Number::from(1))).await.unwrap();
    }

    if let Some(ref mut conf) = hydro.tds_sensor.status_active{
        conf.set_value(Value::Number(Number::from(1))).await.unwrap();
    }

The application works as expected. There might be a prettier way of setting the values instead of the above and I think it would be nice if you could just have methods on the services and characteristics that make more sense instead of the generic 'set_value'. Maybe we could create some macro to generate methods like 'set_active' / 'set_inactive' that could be used within an async update_method to update a service without the need for a running timed loop...?

@ewilken
Copy link
Owner

ewilken commented Aug 14, 2022

Good find!

There might be a prettier way of setting the values instead of the above and I think it would be nice if you could just have methods on the services and characteristics that make more sense instead of the generic 'set_value'. Maybe we could create some macro to generate methods like 'set_active' / 'set_inactive' that could be used within an async update_method to update a service without the need for a running timed loop...?

And good point! Feel free to experiment on that. I'm happy to accept API improvements that make sense to you!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

2 participants