jvmahon / homebridge-hubitat

11 stars 2 forks source link

Occasional polling #24

Closed drewcovi closed 2 years ago

drewcovi commented 3 years ago

As much as I trust websocket updates, I’m frequently in the position where simply restarting homebridge reflects the actual state of Hubitat.

can we introduce a variable for polling the overall state of the home? I just went from seeing one light on to 14 and all I did was restart homebridge for that initial status poll.

jvmahon commented 3 years ago

Actually, its already there. Every 600 seconds, the code requests that Hubitat provide it a full copy of all the device data.

This code is in the module "lib/HubitatSystemObject.js" -- look for line 169-172 and 198-243 of that module.

I have found, however, that it sometimes seems that the iOS Home app itself seems to get out of sync and sometimes I have to restart the iOS Home app by closing (not just minimizing - see: https://support.apple.com/en-us/HT201330) it and then restarting the app.

See, e.g.,: Lines 169-172:

                    var BoundRefreshAll = that.refreshAll.bind(that) // refreshAll needed to be bound to "that" to be passed the correct values when called in setInterval.
                    // that.refreshAll();
                    BoundRefreshAll();

                    // Do a refresh every 10 minutes just to be sure synchronization is maintained with Hubitat.
                    refreshInterval = setInterval(BoundRefreshAll, 600000)

And lines 198-243

    async refreshAll() // Grabs device data from Hubitat just to be sure Homebridge remains in sync. There is no reason it should ever get out of sync, but this is an extra precaution.
    {
        var that = this;
        var deviceID;
        var report = [];    

        HSM.HSMRefresh(that)    // Refresh Home Safety Monitor Intrusion Alert status.  

        this.log(chalk.yellow(`Calling Refresh on each device to update HomeKit with Current Hubitat Device Data.`));

        // Update the allDevices array with the latest data from Maker API.
        that.allDevices =   await fetch(that.deviceURL).then ( response => response.json() );

        // Loop through all the device IDs of devices that want update. Remember, UpdateDeviceTraits is indexed by the Hubitat Device ID #. Note that this is an 'in' loop, so looping through the keys
        for (deviceID in that.UpdateDeviceTraits)
        {
            if (deviceID == 0) continue // HSM will have a 0 deviceID and it has a special refresh handled by HSMRefresh!

            // Want the Hubitat data for this device ID (it's 'fullDeviceData') , but the allDevices array is not indexed by the deviceID, so you need to use the .find() function to get the right entry.
            var fullDeviceData  = this.allDevices.find( function(currentElement) { return (currentElement.id == deviceID)})

            // For a given Hubitat device ID, there may be multiple Services / Characteristics that could be updated. Note that this is an 'of' loop, so looping through the values not keys
            for(var thisHomekitObject of that.UpdateDeviceTraits[deviceID].notifyObjects)
            {
                // For a given Service / Characteristic, it may have registered for one or more Hubitat event attributes(types) - i.e., ("switch", "level", etc). Loop through them.
                // x is the indext to the hubitatAttributesOfInterest arrray
                for(var thisAttributeName of thisHomekitObject.hubitatAttributesOfInterest)
                {
                    // Don't do a refresh on items in the next line! If these were refreshed, they would re-trigger any Stateless ProgrammableSwitches which could trigger HomeKit automations.
                    // You only want to refresh items that are "stateful" -- for those, re-emitting the same event doesn't cause a trigger in HomeKit.
                    if (["pushed", "held", "doubleTapped"].includes(thisAttributeName)) continue

                    // The following line *should* never be triggered as long as the accessory registered the correct listing of attributes for the emitter
                    // But, just in case, a mistake was made, don't emit!
                    if ((fullDeviceData.attributes[thisAttributeName]) === undefined) continue

                    report = {
                        "name": thisAttributeName, 
                        "value": fullDeviceData.attributes[thisAttributeName] 
                        };

                    thisHomekitObject.emit("HubValueChanged", report, thisHomekitObject);
                }
            }
        }
    }
drewcovi commented 3 years ago

So odd! I literally kept it open but restarted homebridge and saw the change… I’ll keep digging then 🙂

jvmahon commented 3 years ago

I've seen that problem too where I've had to restart homebridge to get the iOS app to synchronize. I don't think its the plugin - I've assumed that could be an issue with homebridge itself or possibly with whatever Apple servers / services are in the path between HomeBridge and your actual device. My assumption is it may be an Apple issue to -- I say this because sometimes restarting homebridge causes everything to re-sync, but sometimes it doesn't and then will work 30 minutes later. Its rare enough that I don't worry too much about it.

drewcovi commented 3 years ago

Gotta reopen this. After half a decade maintaining a Vera plug-in (heavily reliant on polling instead of sockets) I can’t point to anything else at fault here. Happy to dig in and make the polling somewhat more reliable even if a configurable value.

jvmahon commented 3 years ago

Happy to have you make a branch and see if you can find anything that needs updating. Good luck.

jvmahon commented 2 years ago

I'm going to close this for now - but if you come up with a better solution, please re-open and let me know.