Apple Homekit Garagedoor emulation

I have been reviewing the existing threads for the Apple Homekit Garagedoor integration and need a little help to put an idea into practice. I have 2 motorized doors operated with Shelly 1 relays connected to the toggle switch of the motors (open/stop/close - the relay automatically disconnects in 1 sec) and using the Shelly with the relevant homekit tag does the trick but with one catch: once the button is pressed in Homekit, the icon changes into opening, then in 1 sec changes to closed (and the same thing happens when I’m closing). What I want to do is 2 things:

  1. Add a timer to mimic the time it takes to open/close the doors
  2. Keep the Homekit button status in sync with the actual status (i.e. if the door is open, the icon should be showing open as well)

Right now this is what I have (it works well with the above catch):

Switch Shelly_Garage				"Garázsajtó"					{channel="shelly:shellyplus1:5185c54319:relay#output", homekit="GarageDoorOpener"}
Switch Shelly_Gate					"Kapu"							{channel="shelly:shellyplus1:4ec4539e84:relay#output", homekit="GarageDoorOpener"}

I understand I can have groups mapped against a single device but can’t figure out how I can have that item working both from OH and from Homekit. So far I have come up with the below but I’m not sure how I could make that work from Homekit - any ideas?

Group gGaragedoor					"Garázsajtó"								{homekit="GarageDoorOpener"}
Switch Shelly_Garage				"Garázsajtó Shelly"							{channel="shelly:shellyplus1:5185c54319:relay#output"}
String Garagedoor_status			"Garázsajtó státusz"	<door>	(gGarage)	{homekit="ContactSensor"}
String Garagedoor_target			"Garage target state"			(gGarage)	{homekit="GarageDoorOpener.TargetDoorState"}
String Garagedoor_state				"Garage current state"			(gGarage)	{homekit="GarageDoorOpener.CurrentDoorState"}

I was thinking on a rule as the door takes 15 seconds to open, here’s my take on that (but I don’t have a contact sensor and I’m not sure which item would be able to work as a trigger for opening the door from Homekit as it’s mapped with the group):

rule "Garazskapu nyit"
when
	    Item Garagedoor_status changed from OFF to ON
then
        Garagedoor_target.postUpdate(ON)
		Shelly_Garage.sendCommand(ON)
		createTimer(now.plusSeconds(15), [ |
			Garagedoor_state.postUpdate(OPEN)
			])
end

Without this your status will at best be an approximation and at worst fall out of sync with the door. In short, you’d never really be able to rely on the Item’s state for anything.

Consider this scenario. Most garage door openers have a sensor (multiple really) where if there is an obstruction, the door will automatically stop and open. So you’ve pressed the button and the door starts to close, the sensor causes it to stop and go back up. Now your status Item says the door is closed but in truth it’s open. And now it will be out of sync forever (showing open when it’s closed and closed when it’s open) until you manually go in and fix the status.

To model a garage door you really need the contact sensors, at least one per door to know when the door is open independent from the motor.

I’m not sure whether the Shelly 1s you have support this (you need a Gen 3+, I only have Gen 2 but I am looking into getting a Gen 4 so I can replace my RPi which is doing this job for me at the moment) but you can hook a reed switch (for example) to the device through the GPIO. For example Garage door control – The unofficial Shelly guide!. And reed switches are super cheap. You’ll probably spend more on the wires.

Of course the sensor doesn’t necessarily need to be the same device as the relay, but it’s convenient.

With a contact here is how I manage my garage doors. I use GA instead of Homekit, but going through the docs for Homekit should help.

My Thing is MQTT:

version: 1
things:
  mqtt:topic:mosquitto:cerberos_sensor_reporter:
    bridge: mqtt:broker:broker
    label: cerberos sensor_reporter
    location: Garage
    config:
      availabilityTopic: sensor_reporter/cerberos/status
      payloadAvailable: ONLINE
      payloadNotAvailable: OFFLINE
    channels:
      garagedoor1:
        type: contact
        label: Large garage door
        description: Large garage door open status
        config:
          stateTopic: sensor_reporter/cerberos/garagedoor1/state
          "on": OPEN
          "off": CLOSED
      garagedoor2:
        type: contact
        label: Small garage door
        description: Small garage door open status
        config:
          stateTopic: sensor_reporter/cerberos/garagedoor2/state
          "on": OPEN
          "off": CLOSED
      garagedoor1_opener:
        type: switch
        label: Large garage door opener
        description: ""
        config:
          commandTopic: sensor_reporter/cerberos/garagedoor1/cmd
          "on": "ON"
          "off": "OFF"
      garagedoor2_opener:
        type: switch
        label: Small garage door opener
        description: Small garage door opener controller
        config:
          commandTopic: sensor_reporter/cerberos/garagedoor2/cmd
          "on": "ON"
          "off": "OFF"
      online:
        type: switch
        label: Online status
        description: Indicates online status of this sensor_reporter
        config:
          stateTopic: sensor_reporter/cerberos/status
          "on": ONLINE
          "off": OFFLINE

Items:

version: 1
items:
  Garage:
    type: Group
    label: Garage
    icon: material:garage
    groups:
      - Ground_Floor
    tags:
      - Garage
  Large_Garagedoor_Sensor:
    type: Contact
    label: Large garage door
    icon: material:garage
    format: '%s'
    groups:
      - Large_Garagedoor
      - DoorsStatus
    tags:
      - OpenState
      - Status
    channels:
      mqtt:topic:mosquitto:cerberos_sensor_reporter:garagedoor1:
        profile: basic-profiles:debounce-time
        toItemDelay: 1000
    metadata:
      doorOpenReminder:
        value: ' '
        config:
          defaultAlertDelay: PT30M
          defaultRemPeriod: PT30M
      listWidget:
        value: widget:rlk_garagedoor_list
        config:
          control_item: Large_Garagedoor_Opener
          name: Large Garage Door
          sensor_item: Large_Garagedoor_Sensor
      matter:
        value: ContactSensor
  Large_Garagedoor:
    type: Group
    label: Large Garage Door
    icon: material:garage
    groups:
      - Garage
    tags:
      - GarageDoor
    metadata:
      listWidget:
        value: ' '
        config:
          action: command
          actionCommand: "ON"
          actionFeedback: Triggered the large garage door
          actionItem: Large_Garagedoor_Opener
          item: Large_Garagedoor_Sensor
      widget:
        value: ' '
        config:
          action: command
          actionCommand: "ON"
          actionItem: Large_Garagedoor_Opener
          icon: oh:garagedoor
          iconUseState: true
          item: Large_Garagedoor_Sensor
          label: "=(items.Large_Garagedoor_Sensor.state == \"OPEN\") ? \"open\" : \"closed\""
          title: Large Garage Door Opener
  Large_Garagedoor_Opener:
    type: Switch
    label: Large garage door opener
    icon: material:settings_remote
    format: '%s'
    groups:
      - Large_Garagedoor
    channel: mqtt:topic:mosquitto:cerberos_sensor_reporter:garagedoor1_opener
    metadata:
      ga:
        value: Garage
      listWidget:
        value: oh-list-item
        config:
          action: command
          actionCommand: "ON"
          actionItem: Large_Garagedoor_Opener
          badge: "=(items.Large_Garagedoor_Sensor.state == \"CLOSED\") ? \"closed\" : \"open\""
          badgeColor: "=(items.Large_Garagedoor_Sensor.state == \"CLOSED\") ? \"green\" : \"red\""
          icon: "=(items.Large_Garagedoor_Sensor.state == \"CLOSED\") ? \"f7:house\" : \"f7:house_fill\""
          iconColor: "=(items.Large_Garagedoor_Sensor.state == \"CLOSED\") ? \"green\" : \"red\""
          listButton: false
          title: Large Garage Door
      matter:
        value: OnOffPlugInUnit
  Small_Garagedoor_Sensor:
    type: Contact
    label: Small garage door
    icon: material:garage
    format: '%s'
    groups:
      - Small_Garagedoor
      - DoorsStatus
    tags:
      - OpenState
      - Status
    channels:
      mqtt:topic:mosquitto:cerberos_sensor_reporter:garagedoor2:
        profile: basic-profiles:debounce-time
        toItemDelay: 1000
    metadata:
      doorOpenReminder:
        value: ' '
        config:
          defaultAlertDelay: PT90M
          defaultRemPeriod: PT90M
      listWidget:
        value: widget:rlk_garagedoor_list
        config:
          control_item: Small_Garagedoor_Opener
          name: Small Garage Door
          sensor_item: Small_Garagedoor_Sensor
      matter:
        value: ContactSensor
  Small_Garagedoor_Opener:
    type: Switch
    label: Small garage door opener
    icon: material:settings_remote
    format: '%s'
    groups:
      - Small_Garagedoor
    channel: mqtt:topic:mosquitto:cerberos_sensor_reporter:garagedoor2_opener
    metadata:
      ga:
        value: Garage
      listWidget:
        value: widget:rlk_garagedoor_list
      matter:
        value: OnOffPlugInUnit
  Small_Garagedoor:
    type: Group
    label: Small Garagedoor
    icon: material:toggle_on
    groups:
      - Garage
    tags:
      - GarageDoor
    metadata:
      listWidget:
        value: ' '
        config:
          action: command
          actionCommand: "ON"
          actionFeedback: Triggered the small garage door
          actionItem: Small_Garagedoor_Opener
          item: Small_Garagedoor_Sensor
      widget:
        value: ' '
        config:
          action: command
          actionCommand: "ON"
          actionItem: Small_Garagedoor_Opener
          icon: oh:garagedoor
          iconUseState: true
          item: Small_Garagedoor_Sensor
          label: "=(items.Small_Garagedoor_Sensor.state == \"OPEN\") ? \"open\" : \"closed\""
          title: Small Garage Door Opener

The main thing is I have a separate Item for the status and to trigger the openers.

My MainUI widget:

uid: garage_widget
tags:
  - card
  - garage
props:
  parameters: []
  parameterGroups: []
timestamp: Sep 20, 2023, 10:58:06 AM
component: f7-card
config:
  title: '=(items.GarageCamera_Status.state === "ON") ? "Garage Doors" : "Garage
    Doors: Camera Offline!"'
slots:
  default:
    - component: f7-row
      config:
        class:
          - justify-content-center
      slots:
        default:
          - component: f7-col
            config:
              width: auto
            slots:
              default:
                - component: oh-image-card
                  config:
                    refreshInterval: 3000
                    style:
                      height: auto
                      width: 100%
                    url: '=(items.GarageCamera_Status.state === "ON") ?
                      items.GarageWyzeCamera_ImageURL.state :
                      "/static/garage.jpg"'
    - component: f7-row
      config:
        class:
          - justify-content-left
      slots:
        default:
          - component: f7-col
            slots:
              default:
                - component: oh-label-card
                  config:
                    action: command
                    actionCommand: ON
                    actionItem: Small_Garagedoor_Opener
                    footer: Small Garage Door Opener
                    icon: '=(items.Small_Garagedoor_Sensor.state == "CLOSED") ? "f7:house" :
                      "f7:house_fill"'
                    iconColor: '=(items.Small_Garagedoor_Sensor.state == "CLOSED") ? "green" :
                      "orange"'
                    item: Small_Garagedoor_Sensor
                    label: '=(items.Small_Garagedoor_Sensor.state == "OPEN") ? "close" : "open"'
          - component: f7-col
            slots:
              default:
                - component: oh-label-card
                  config:
                    action: command
                    actionCommand: ON
                    actionItem: Large_Garagedoor_Opener
                    footer: Large Garage Door Opener
                    icon: '=(items.Large_Garagedoor_Sensor.state == "CLOSED") ? "f7:house" :
                      "f7:house_fill"'
                    iconColor: '=(items.Large_Garagedoor_Sensor.state == "CLOSED") ? "green" :
                      "orange"'
                    label: '=(items.Large_Garagedoor_Sensor.state == "OPEN") ? "close" : "open"'

I also have a camera to monitor the doors.

Rules:

configuration: {}
triggers:
  - id: "1"
    configuration:
      itemName: Large_Garagedoor_Sensor
    type: core.ItemStateChangeTrigger
  - id: "2"
    configuration:
      itemName: Small_Garagedoor_Sensor
    type: core.ItemStateChangeTrigger
conditions: []
actions:
  - inputs: {}
    id: "3"
    configuration:
      type: application/javascript
      script: >-
        const opener = items[event.itemName].semantics.equipment.members.find(i
        => i.type == "Switch");

        console.debug('Found garage door opener Item ' + opener.name);


        opener.postUpdate((event.newState == "CLOSED") ? 'OFF' : 'ON');
    type: script.ScriptAction

GA treats the ON/OFF status of the control Item to indicate the OPEN/CLOSED status of the door, so this rule keeps that Item in sync with the contact sensor. Any command to the control Item causes is interpreted as a button press by the controller with the relay connected to the opener button.

1 Like

Thank you, this is really helpful. I have Shelly 1 gen 4 and it’s on my todo list to add reed relays to have a more reliable way to confirm if it’s closed or not as I do not have cameras and this effort would be preparing for that actually. I was thinking that once I have the sensor, I will be able to adjust the rule to report the obstruction if the reed relay does not signal after the time to close the door has been passed. I will review this in greater depth and get back.

So I have gotten a bit further, also I have installed reed relays to monitor the open/closed status of the door but need a bit more help in how to wire these together for HomeKit.

This is my items:

Switch Shelly_Garage				"Garagedoor"					{channel="shelly:shellyplus1:5185c54319:relay#output", homekit="GarageDoorOpener"}
Switch Shelly_Garage_input			"Garagedoor switch input"		{channel="shelly:shellyplus1:5185c54319:relay#input"}
Contact Garagedoor_status			"Garagedoor status"				{homekit="ContactSensor"}
//Switch Shelly_Garage_target		"Garage target state"			{homekit="GarageDoorOpener.TargetDoorState"}
//String Shelly_Garage_state		"Garage current state"			{homekit="GarageDoorOpener.CurrentDoorState"}
Number Shelly_Garage_AutoOffTimer 	"Garagedoor Auto Off Timer"		{channel="shelly:shellyplus1:5185c54319:relay#autoOff"}

Also I have a rule to convert the input switch into a contact sensor:

rule "Convert Garage Switch to Contact"
when
    Item Shelly_Garage_input changed
then
    if (Shelly_Garage_input.state == ON) {
        Garagedoor_status.postUpdate("CLOSED")
    } else {
        Garagedoor_status.postUpdate("OPEN")
    }
end

How do I group these together in such a way for Apple Homekit to both control the door & also to correctly represent the status (open/closed)? I have seen in examples that people use a Group for the GarageDoorOpener item but I’m not sure what to put where; I guess I will need the relay and the contact sensor as bare minimum, right? I primarily want to control the garage via Homekit not OH.