dkerr64 / homebridge-yolink

Apache License 2.0
12 stars 2 forks source link

Add support for Power Failure Alarm #53

Closed dkerr64 closed 1 year ago

dkerr64 commented 2 years ago

Add support for PowerFailureAlarm. Sample JSON...

{
    "event": "PowerFailureAlarm.Report",
    "time": 1662985976448,
    "msgid": "1662985976447",
    "data": {
        "state": "normal",
        "sound": 1,
        "battery": 4,
        "powerSupply": true,
        "alertDuration": 30,
        "alertInterval": 5,
        "version": "0608",
        "loraInfo": {
            "signal": -43,
            "gatewayId": "xxxx",
            "gateways": 1
        }
    },
    "deviceId": "xxxx"
}
dkerr64 commented 2 years ago

@wojo I have fixed the fatal error on initialization. Please download version 1.2.8.

Now I would like to add support for the power fail alarm but there is no documentation for this at the YoLink website so I will need your help to get started. I noted the MQTT message received but I will need more. If you run the plugin and then generate events (like power failure alert) then these should be logged by my plugin. They will be logged as warnings for unsupported device (appear in orange in the log). Can you please send me whatever is logged as warnings (you do not need verbose log set). Try and generate whatever messages you can please (sometimes making settings updates from the YoLink app generates MQTT messages to listeners). Most important however is to see what it sends on power failure and power restoration.

Thanks.

dkerr64 commented 2 years ago

@wojo of course the other problem is how to represent this sensor in HomeKit, it does not have the concept of a power failure sensor. I could substitute another sensor type, for example motion, contact, occupancy sensor. Or I could represent it by a powered device like a switch, lightbulb or outlet (which would not be controllable, but could be monitored/queried for powered on or not). Any of the above would be a compromise.

wojo commented 2 years ago

@dkerr64 Events below. Note alertDuration is seconds, alertInterval is minutes.

When AC power (well USB power, but to mains) is lost it sends a PowerFailureAlarm.Alert with "state": "alert" "powerSupply": false.

{
    "event": "PowerFailureAlarm.Alert",
    "time": 1663029430659,
    "msgid": "1663029430658",
    "data": {
        "state": "alert",
        "sound": 1,
        "battery": 4,
        "powerSupply": false,
        "alertDuration": 30,
        "alertInterval": 5,
        "version": "0608",
        "loraInfo": {
            "signal": -47,
            "gatewayId": "xxx",
            "gateways": 4
        }
    },
    "deviceId": "xxx"
}

If the alert is re-triggered due to the alertInterval hitting, you get the same event and "state": "alert":

{
    "event": "PowerFailureAlarm.Alert",
    "time": 1663031451115,
    "msgid": "1663031451115",
    "data": {
        "state": "alert",
        "alertType": "remind",
        "sound": 1,
        "battery": 4,
        "powerSupply": false,
        "alertDuration": 10,
        "alertInterval": 1,
        "version": "0608",
        "loraInfo": {
            "signal": -37,
            "gatewayId": "xxx",
            "gateways": 4
        }
    },
    "deviceId": "xxx"
}

When power is restored, a PowerFailureAlarm.StatusChange is sent with "state": "normal" and "powerSupply" : true:

{
    "event": "PowerFailureAlarm.StatusChange",
    "time": 1663029928110,
    "msgid": "1663029928109",
    "data": {
        "state": "normal",
        "sound": 1,
        "battery": 4,
        "powerSupply": true,
        "alertDuration": 30,
        "alertInterval": 5,
        "version": "0608",
        "loraInfo": {
            "signal": -31,
            "gatewayId": "xxx",
            "gateways": 4
        }
    },
    "deviceId": "xxx"
}

There's a hard switch on the side which turns off the unit (off), and then three settings for on (L/M/H). These come through as "state": "off" or "state": "normal", and then sound integer values 1-3 mapping to L/M/H.

{
    "event": "PowerFailureAlarm.StatusChange",
    "time": 1663029788434,
    "msgid": "1663029788433",
    "data": {
        "state": "off",
        "sound": 3,
        "battery": 4,
        "powerSupply": true,
        "alertDuration": 30,
        "alertInterval": 5,
        "version": "0608",
        "loraInfo": {
            "signal": -24,
            "gatewayId": "xxx",
            "gateways": 4
        }
    },
    "deviceId": "xxx"
}

Setting options in the app results in a PowerFailureAlarm.setOption on next check-in (when the device is awake) for alertDuration in seconds (10, 30, 60, 120, 300, 600, 900, 1200, 3600) and alertInterval in minutes (0, 1, 2, 5, 10, 15, 30, 60, 120, 240):

{
    "event": "PowerFailureAlarm.setOption",
    "time": 1663030338998,
    "msgid": "1663030338998",
    "data": {
        "alertDuration": 30,
        "alertInterval": 5,
        "loraInfo": {
            "signal": -34,
            "gatewayId": "xxx",
            "gateways": 4
        }
    },
    "deviceId": "xxx"
}

Hitting the physical Set button just sends a PowerFailureAlarm.Report with nothing interesting, but it does make the alarm sound for a few seconds as a test:

{
    "event": "PowerFailureAlarm.Report",
    "time": 1663030738694,
    "msgid": "1663030738694",
    "data": {
        "state": "normal",
        "sound": 1,
        "battery": 4,
        "powerSupply": true,
        "alertDuration": 30,
        "alertInterval": 5,
        "version": "0608",
        "loraInfo": {
            "signal": -30,
            "gatewayId": "xxx",
            "gateways": 4
        }
    },
    "deviceId": "xxx"
}

Lastly I tried pulling batteries but couldn't get it to send anything, so I guess it requires batteries to have lower voltage to trigger the battery like all the other devices down from 4..0 for 100% to 0%.

wojo commented 2 years ago

@wojo of course the other problem is how to represent this sensor in HomeKit, it does not have the concept of a power failure sensor. I could substitute another sensor type, for example motion, contact, occupancy sensor. Or I could represent it by a powered device like a switch, lightbulb or outlet (which would not be controllable, but could be monitored/queried for powered on or not). Any of the above would be a compromise.

Here are some initial thoughts:

The most clear and simple mapping would be a contact sensor that turns "on" when power is lost representing an alarm state, which can be used to control a scene, trigger an automation, etc. When power is restored, it turns "off".

What could be interesting is a temporal/timed contact sensor that retains the behavior of the alertDuration for the "on" state matching the alertDuration. If an alert triggers or is re-triggered via a reminder, it would stay "on" for alertDuration seconds and then automatically turn "off" to assist with triggering an automation on an interval, getting a HomeKit reminder (perhaps a bit odd when it turns "off"), etc. This can also be done using dummy switches Homebridge, so probably best to not implement this.

A switch/outlet (representing mains power) would not be as useful for this type of device, which would never be switched liked you said, and you really only care if it alarms on loss of power. Using these types to represent loss of power as "on" would be very confusing.

dkerr64 commented 2 years ago

@wojo All the JSON is really helpful. Thank you.

On HomeKit service type...

A lightbulb/outlet doesn't feel odd to me. If they are in 'on' state then power is healthy, 'off' would represent a power failure. HomeKit automations can be triggered on change of state from one to other.

YoLink sensors (leak/vibration/motion) report their state as normal or alert similar to what we see here. And the door sensor (which maps to a HomeKit contact sensor) as open or closed where open would be considered 'bad'.

I really don't know what the best answer is, which is probably why many other plugins provide users a choice on how they want a device represented in Homebridge/HomeKit. But I am leaning more towards outlet as it either powered on or off and we can map the YoLink sensor to these states. The icon that appears in Apple Home also more closely resembles what a user might expect for power.

I will not act on alertDuration, that is best left up to other plugins or HomeKit native. I think a MQTT is sent when the alert duration expires, resetting the state to normal, so the plugin will receive that and update the status... at least that is how motion/vibration sensors work.

wojo commented 2 years ago

That makes sense, if choosing one it's easy to still use it in any other way via plugins or HomeKit.

Pretty sure however that the device never returns to normal state and only continues to fire with alert and alertType of remind. Makes sense the even never stopped, unlike a motion sensor.

So in order to trigger something on every remind, something else would have to be exposed like a single shot timed alarm trigger. HomeKit can't do perpetual or long timers well.

The first exposing of power state for the mains power that the device is monitoring is a great step though.

On Mon, Sep 12, 2022, at 22:25, David Kerr wrote:

@wojo https://github.com/wojo All the JSON is really helpful. Thank you.

On HomeKit service type...

A lightbulb/outlet doesn't feel odd to me. If they are in 'on' state then power is healthy, 'off' would represent a power failure. HomeKit automations can be triggered on change of state from one to other.

YoLink sensors (leak/vibration/motion) report their state as normal or alert similar to what we see here. And the door sensor (which maps to a HomeKit contact sensor) as open or closed where open would be considered 'bad'.

I really don't know what the best answer is, which is probably why many other plugins provide users a choice on how they want a device represented in Homebridge/HomeKit. But I am leaning more towards outlet as it either powered on or off and we can map the YoLink sensor to these states. The icon that appears in Apple Home also more closely resembles what a user might expect for power.

I will not act on alertDuration, that is best left up to other plugins or HomeKit native. I think a MQTT is sent when the alert duration expires, resetting the state to normal, so the plugin will receive that and update the status... at least that is how motion/vibration sensors work.

— Reply to this email directly, view it on GitHub https://github.com/dkerr64/homebridge-yolink/issues/53#issuecomment-1244815065, or unsubscribe https://github.com/notifications/unsubscribe-auth/AAAGBM2AYDDQRAV4R34CNUDV57QY7ANCNFSM6AAAAAAQK2P75Y. You are receiving this because you were mentioned.Message ID: @.***>

dkerr64 commented 2 years ago

@wojo I have added initial support for this device. I do not know how well it will work, but it is a start. I implemented a Outlet which should remain in the "on" state whenever power is present. In a power failure it should turn to "off"

I may add options to allow the user to specify what type of device they want this to show up as, but for now I need to make sure that I am handling the messages that are sent.

One thing missing from all the JSON you provided is the initial response to a status request... not what is pushed to us as a MQTT message, but what is returned when we query it. Can you search your log for a PowerFailureAlarm.getState response received please. Should be near the start of the log as part of initial setup.

Now that I have added support in 1.2.9 you will not see the JSON sent/received unless you turn on verbose log.

Thanks

dkerr64 commented 2 years ago

Oh... forgot to add... you need to set enableExperimental to true for now.

wojo commented 2 years ago

@dkerr64 was already testing this as I saw the new device pop up!

State correctly change from "on" and "off" on power loss. I wasn't able to test the re-alert/remind, yet. Will do that soon.

Setting state in HomeKit results in an error in the logs: "Error in OutletDevice handleGet". The outlet turns off, I assume the plugin refresh or any other status message will turn it back on, but it'll stay in this state until then? Does HomeKit have a concept/capability of read only?

PowerFailureAlarm.getState message:

{
    "code": "000000",
    "time": 1663120072812,
    "msgid": 1663120072812,
    "method": "PowerFailureAlarm.getState",
    "desc": "Success",
    "data": {
        "online": true,
        "state": {
            "alertDuration": 30,
            "alertInterval": 60,
            "alertType": null,
            "battery": 4,
            "powerSupply": true,
            "sound": 1,
            "state": "normal",
            "version": "0608"
        },
        "deviceId": "xxx",
        "reportAt": "2022-09-14T01:35:14.157Z"
    }
}
dkerr64 commented 2 years ago

Thank you @wojo based on that JSON I can see that some things will not be working right. I will fix this evening.

Re-alerts, reminds, etc. are not being handled yet.

dkerr64 commented 2 years ago

@wojo please try 1.2.10 that I just published. It should be a lot better. I also allow for either a Outlet or Contact sensor device (defaults to outlet). To change to contact sensor add "powerFailureSensorAs": "Contact" to the config.

Please test and let me know how it goes.

dkerr64 commented 2 years ago

Also, I don't know whether to do anything with alert / reminders. If there is a fault and one of those comes in then I suppose it is possible to briefly toggle the state from fault to ok to fault again. But is that worthwhile? And if I do it for this sensor, then I would need to do it for all other YoLink devices that send similar reminders.

I am really not sure that this should be a function of this plugin. A separate timer could be used to monitor how long a sensor is in fault state and act on it.

wojo commented 1 year ago

@dkerr64 contact sensor looks good.

I agree that the remind shouldn't toggle the state, but if it was toggle to "off" somehow then it could probably return it to "on" to keep it accurate to the actual state. For anyone that wants that, there will be another that doesn't of course and then you are talking another setting...

That said for those that really want to have it match the state always, just use the new Contact Sensor mode :)

dkerr64 commented 1 year ago

@wojo I'm going to declare this working now and close this issue. If any problems come up please open new bug issue.

Huge THANK YOU for helping me implement support for this device.

wojo commented 1 year ago

Agree and any time!