OpenSprinkler Forums Pictures and Creative Use Updated OpenGarage firmware to allow for the use of a wireless contact sensor

  • This topic has 2 replies, 2 voices, and was last updated 2 days ago by 3.14.
Viewing 3 posts - 1 through 3 (of 3 total)
  • Author
    Posts
  • #87070

    3.14
    Participant

    I’ve wanted to get this done for a while and it is working well.

    This works in Home Assistant and required a MQTT broker to be installed, as well as an automation (found below) to publish the sensor status to the MQTT broker.

    I’ve attached the updated FW file and instructions to compile the changes on your own (so you can see what has been changed)

    Once you recompile the FW and turn on MQTT support in OG (adding IP address and username/password for your MQTT broker) and enable the wired sensor OG will get the status from the MQTT broker.

    Enjoy!

    Questions are welcome.

    Save the code below to a file and run the Python command to make the required changes to the main. py.cpp file.

    #Start of FILE TO SAVE
    #!/usr/bin/env python3
    from pathlib import Path
    import shutil
    import sys

    if len(sys.argv) != 2:
    raise SystemExit(“Usage: python3 patch_opengarage_mqtt_contact.py /path/to/main.cpp”)

    source = Path(sys.argv[1])
    if not source.is_file():
    raise SystemExit(f”File not found: {source}”)

    backup = source.with_suffix(source.suffix + “.bak”)
    if backup.exists():
    raise SystemExit(f”Backup already exists: {backup}”)

    text = source.read_text(encoding=”utf-8″)

    def replace_once(old: str, new: str, label: str) -> None:
    global text
    count = text.count(old)
    if count != 1:
    raise SystemExit(f”{label}: expected one matching section, found {count}. ”
    “Use the unmodified OpenGarage 1.2.4 main.cpp.”)
    text = text.replace(old, new, 1)

    replace_once(
    “””String mqtt_topic;
    String mqtt_id;

    static String scanned_ssids;”””,
    “””String mqtt_topic;
    String mqtt_id;

    // External wireless door-contact sensor, supplied over MQTT.
    // The publisher must retain its state and refresh it at least once per minute.
    static const char* MQTT_CONTACT_TOPIC = “garage/door/contact”;
    static const ulong MQTT_CONTACT_TIMEOUT_MS = 180000UL;
    static bool mqtt_contact_known = false;
    static bool mqtt_contact_open = false;
    static ulong mqtt_contact_last_seen_ms = 0;

    static String scanned_ssids;”””,
    “MQTT contact state declarations”,
    )

    replace_once(
    “””// MQTT callback to read “Button” requests
    void mqtt_callback(char* topic, byte* payload, unsigned int length) {
    \tpayload[length]=0;
    \tString Payload((char*)payload);
    \tString Topic(topic);

    \t//Accept button on any topic for backwards compat with existing code – use IN messages below if possible”””,
    “””// MQTT callback to read “Button” requests
    void mqtt_callback(char* topic, byte* payload, unsigned int length) {
    \tpayload[length]=0;
    \tString Payload((char*)payload);
    \tString Topic(topic);

    \t// External MQTT door-contact sensor.
    \t// OPEN means the door is not fully closed; CLOSED confirms it is fully closed.
    \tif (Topic == MQTT_CONTACT_TOPIC) {
    \t\tif (Payload == “OPEN”) {
    \t\t\tmqtt_contact_open = true;
    \t\t\tmqtt_contact_known = true;
    \t\t\tmqtt_contact_last_seen_ms = millis();
    \t\t\tDEBUG_PRINTLN(F(“MQTT contact sensor: OPEN”));
    \t\t} else if (Payload == “CLOSED”) {
    \t\t\tmqtt_contact_open = false;
    \t\t\tmqtt_contact_known = true;
    \t\t\tmqtt_contact_last_seen_ms = millis();
    \t\t\tDEBUG_PRINTLN(F(“MQTT contact sensor: CLOSED”));
    \t\t} else {
    \t\t\tDEBUG_PRINT(F(“MQTT contact sensor: invalid payload: “));
    \t\t\tDEBUG_PRINTLN(Payload);
    \t\t}
    \t\treturn;
    \t}

    \t//Accept button on any topic for backwards compat with existing code – use IN messages below if possible”””,
    “MQTT callback”,
    )

    replace_once(
    “”” mqttclient.subscribe(mqtt_topic.c_str());
    mqttclient.subscribe((mqtt_topic +”/IN/#”).c_str());
    mqttclient.publish((mqtt_topic+”/OUT/STATUS”).c_str(), “online”, true);”””,
    “”” mqttclient.subscribe(mqtt_topic.c_str());
    mqttclient.subscribe((mqtt_topic +”/IN/#”).c_str());
    mqttclient.subscribe(MQTT_CONTACT_TOPIC);
    mqttclient.publish((mqtt_topic+”/OUT/STATUS”).c_str(), “online”, true);”””,
    “MQTT subscription”,
    )

    replace_once(
    “”” // Read SN2 — optional switch sensor
    sn2_value = og.get_switch();
    byte sn2_status = 0;
    if(og.options[OPTION_SN2].ival == OG_SN2_NC) { // if SN2 is normally closed type
    sn2_status = sn2_value;
    } else if(og.options[OPTION_SN2].ival == OG_SN2_NO) { // if SN2 is normally open type
    sn2_status = 1-sn2_value;
    }

    switch (og.options[OPTION_SECV].ival) {“””,
    “”” // Read SN2 — optional switch sensor
    byte sn2_status = 0;
    bool mqtt_contact_fresh =
    mqtt_contact_known &&
    ((ulong)(millis() – mqtt_contact_last_seen_ms) <= MQTT_CONTACT_TIMEOUT_MS);

    if (og.options[OPTION_SN2].ival > OG_SN2_NONE) {
    // Replace the physical switch input with MQTT contact state.
    // sn2_value is preserved for the web/API/MQTT JSON status output.
    if (mqtt_contact_fresh) {
    sn2_status = mqtt_contact_open ? 1 : 0;
    sn2_value = sn2_status;
    } else {
    sn2_value = 255; // invalid / stale sensor value
    }
    } else {
    // Preserve original behavior when the switch sensor is disabled.
    sn2_value = og.get_switch();
    }

    switch (og.options[OPTION_SECV].ival) {“””,
    “switch-sensor input”,
    )

    replace_once(
    “”” default: // No secplus
    // Process Sensor Logic
    bool status = false;”””,
    “”” default: // No secplus
    // Do not claim a confirmed state if the enabled MQTT contact
    // sensor is absent or stale.
    if (og.options[OPTION_SN2].ival > OG_SN2_NONE && !mqtt_contact_fresh) {
    door_status = DOOR_STATUS_UNKNOWN;
    break;
    }

    // Process Sensor Logic
    bool status = false;”””,
    “stale-sensor handling”,
    )

    shutil.copy2(source, backup)
    source.write_text(text, encoding=”utf-8″)
    print(f”Patched: {source}”)
    print(f”Backup: {backup}”)`

    #END OF FILE TO SAVE

    You will need to add this automation to your Home Assistant system (please change the sensor names to match your sensor):

    alias: Publish garage-door contact to OpenGarage
    triggers:
    – trigger: state
    entity_id: binary_sensor.garage_door_window
    – trigger: homeassistant
    event: start
    – trigger: time_pattern
    minutes: “/1”

    conditions:
    – condition: template
    value_template: >
    {{ states(‘binary_sensor.garage_door_window’) in [‘on’, ‘off’] }}

    actions:
    – action: mqtt.publish
    data:
    topic: garage/door/contact
    payload: >
    {{ ‘OPEN’ if is_state(‘binary_sensor.garage_door_window’, ‘on’) else ‘CLOSED’ }}
    qos: 1
    retain: true

    mode: restart

    #87074

    Ray
    Keymaster

    Thanks for sharing your work!

    #87087

    3.14
    Participant

    The command line command to modify the main.cpp file follows, please name the file you create from the information above ‘patch_opengarage_mqtt_contact.py’

    ‘python3 patch_opengarage_mqtt_contact.py OpenGarage/main.cpp’

Viewing 3 posts - 1 through 3 (of 3 total)
  • You must be logged in to reply to this topic.

OpenSprinkler Forums Pictures and Creative Use Updated OpenGarage firmware to allow for the use of a wireless contact sensor