Bouke / HAP

Swift implementation of the Homekit Accessory Protocol
https://boukehaarsma.nl/HAP/
MIT License
364 stars 50 forks source link

Question: update when controller is viewing? #96

Closed Bouke closed 4 years ago

Bouke commented 4 years ago

User's question:

As I'm polling the heat pump api, there can be a time between when I last polled, a change being made via another interface and opening the App - which means the App has stale data. I could poll more frequently - but this seems pointless given 99% of the time it'll be overkill.

What I'm trying to do is detect when the user opens the app and the HAP-Server is first queried for a status update, so I can initiate a poll and update ASAP. This may mean there is a couple of seconds before it's refreshed, but this is liveable.

I can see from the logs that there is a "GET /characteristics" call every time a client reconnects. Is there a way to detect this in the 'main.swift' so that I can take appropriate actions ?

Bouke commented 4 years ago

This is related to this: #13. It's not necessarily subscribing, but looking up. Currently there's no callback for this, but it might be worth considering to add this. Mind you; you still need to poll your device for status, as you will still be serving stale data initially. For my home, I just poll the device every 30 seconds, which is often enough to not be considered stale.

You could add a callback to characteristics() endpoint here: https://github.com/Bouke/HAP/blob/82f681166ab2c5b4e0e4ef8b51dc9088e5d09013/Sources/HAP/Endpoints/characteristics().swift#L91.

kentnz commented 4 years ago

Thanks Bouke,

This is what I ended up doing to get this working.

in the .GET for characteristics().swift, after the 'responses.append(response) about line 90

            }
            responses.append(response)

            device.controllerDidGetCharacteristics(channel, forCharacteristic: characteristic)
        }

in 'Device.swift', I found the 'addSubscriber' func about line 400, duplicated the code and renamed both the 'func' and also the 'delegate?.'

 // Add an object which would be notified of changes to Characterisics
func controllerDidGetCharacteristics(_ channel: Channel, forCharacteristic characteristic: Characteristic) {
    subscribersSyncQueue.sync {
        if !characteristicSubscribers.keys.contains(ObjectIdentifier(characteristic)) {
            characteristicSubscribers[ObjectIdentifier(characteristic)] = []
        }
        characteristicSubscribers[ObjectIdentifier(characteristic)]!.insert(ObjectIdentifier(channel))
    }

    if let service = characteristic.service, let accessory = service.accessory {
        delegate?.controllerDidGetCharacteristics(accessory,
                                                     service: service,
                                                     characteristic: AnyCharacteristic(characteristic))
    }
}

in 'DeviceDelegate.swift' I added the following two chunks (near top and about line 80)

public protocol DeviceDelegate: class { func controllerDidGetCharacteristics( _ accessory: Accessory, service: Service, characteristic: AnyCharacteristic )

public extension DeviceDelegate { func controllerDidGetCharacteristics( _ accessory: Accessory, service: Service, characteristic: AnyCharacteristic) { }

Finally, back in my 'main.swift' I added the following into the 'DeviceDelegate' class.

class MyDeviceDelegate: DeviceDelegate { func controllerDidGetCharacteristics(_ accessory: Accessory, service: Service, characteristic: AnyCharacteristic) { logger.info("Client GET - initiate refresh") }

This appears to be working - as when the user opens the App, the first thing it does it GET /characteristics which then triggers the DeviceDelegate call, so I can initiate a data refresh.

Kent.