Mimolite: Simple Integration of Smarthings with Hardwired Security System

I have 1 Semi-old hardwired security system reporting to a monitoring company and
1 Smartthings Security System with a few motion sensors reporting to my phone
I wanted to merge the two ie have security alarm reported by smartthings translate to a security alarm of the hardwired system and have the security alarm reported by Hardwired system translate to a smarthings alarm.

Steps:

  1. Free up a zone on Hardwired system: I did this by connecting 2 zones (zone 2 and 5) in series ie. twisting together common of Zone 2 and Zone wire of Zone 5. This freed up zone 5

  2. Set up a device handler for mimolite. (Pasting my modified device handler with a lot of configurable options) at the end of this post. Then pair mimolite with smarthings

  3. Set up a security rule to turn on Mimolite switch when a motion sensor is triggered

  4. Send Smartthings alarm event to Hardwired System: Connect the NC of Mimolite to Zone 5 pin and Common to common pin. This will trigger the alarm when Mimolite switch is turned on.

  5. Send Hardwired security alarm event to Smartthings: Connect the Sig+ to Bell+ in alarm panel and Sig- to Bell- in alarm pannel

  6. Set up a custom rule to monitor open/close sensor of mimolite (which will be opened when the bell on harwired panel goes off)

  7. Measure the open and closed voltage across bell terminals of hardwired system. For my system it was 2.5v and 13.5v. So i set my mimolite analog triggers between 3.5v and 14.5v (198,210,254,255 parameter values in mimolite), see the example code for more details.

  8. Modify the parameters needed on the handler depending on functionality you need and voltage you see.

    /**

    • FortrezZ Flow Meter Interface

    • Copyright 2016 FortrezZ, LLC

    • Licensed under the Apache License, Version 2.0 (the “License”); you may not use this file except

    • in compliance with the License. You may obtain a copy of the License at:

    •  http://www.apache.org/licenses/LICENSE-2.0
      
    • Unless required by applicable law or agreed to in writing, software distributed under the License is distributed

    • on an “AS IS” BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License

    • for the specific language governing permissions and limitations under the License.

    • Based on Todd Wackford’s MimoLite Garage Door Opener
      */
      metadata {
      // Automatically generated. Make future change here.
      definition (name: “FortrezZ MIMOlite Generic”, namespace: “fortrezz”, author: “FortrezZ, LLC”) {
      capability "Configuration"
      capability "Switch"
      capability "Refresh"
      capability "Contact Sensor"
      capability “Voltage Measurement”

      attribute "powered", "string"
      
      command "on"
      command "off"
      
      fingerprint deviceId: "0x1000", inClusters: "0x72,0x86,0x71,0x30,0x31,0x35,0x70,0x85,0x25,0x03"
      

      }

      simulator {
      // Simulator stuff

      }

      preferences {
      //input “RelaySwitchDelay”, “decimal”, title: “Delay between relay switch on and off in seconds. Only Numbers 0 to 3.0 allowed. 0 value will remove delay and allow relay to function as a standard switch”, description: “Numbers 0 to 3.1 allowed.”, defaultValue: 0, required: false, displayDuringSetup: true

      input “Group1”, “decimal”, title: “Basic set command on Trigger/UnTrigger True(1)/False(0)”, description: " True(1)/False(0)", defaultValue: 0, required: false, displayDuringSetup: true
      input “Group2”, “decimal”, title: “Periodic multiLevel sensor report based on Param9 True(1)/False(0)”, description: " True(1)/False(0)", defaultValue: 0, required: false, displayDuringSetup: true
      input “Group3”, “decimal”, title: “Alarm command class report for power droupout True(1)/False(0)”, description: " True(1)/False(0)", defaultValue: 1, required: false, displayDuringSetup: true
      input “Group4”, “decimal”, title: “Binary sensor command on Trigger/UnTrigger True(1)/False(0)”, description: " True(1)/False(0)", defaultValue: 1, required: false, displayDuringSetup: true
      input “Group5”, “decimal”, title: “Pulse meter count based on Param9 True(1)/False(0)”, description: " True(1)/False(0)", defaultValue: 0, required: false, displayDuringSetup: true

      input “Param2”, “decimal”, title: “Reset Pulse Count True(1)/False(0)”, description: " True(1)/False(0)", defaultValue: 1, required: false, displayDuringSetup: true
      input “Param3”, “decimal”, title: “Relay Triggered by SIG1 True(1)/False(0)”, description: " True(1)/False(0)", defaultValue: 0, required: false, displayDuringSetup: true
      input “Param4”, “decimal”, title: “LowerThreshold High”, description: “Numbers 0 to 255 allowed.”, defaultValue: 210, required: false, displayDuringSetup: true
      input “Param5”, “decimal”, title: “LowerThreshold Low”, description: “Numbers 0 to 255 allowed.”, defaultValue: 198, required: false, displayDuringSetup: true
      input “Param6”, “decimal”, title: “HigherThreshold High”, description: “Numbers 0 to 255 allowed.”, defaultValue: 255, required: false, displayDuringSetup: true
      input “Param7”, “decimal”, title: “HigherThreshold Low”, description: “Numbers 0 to 255 allowed.”, defaultValue: 254, required: false, displayDuringSetup: true
      input “Param8_1”, “decimal”, title: “Analog(0)/Digital(1) Trigger”, description: “Analog(0)/Digital(1)”, defaultValue: 0, required: false, displayDuringSetup: true
      input “Param8_0”, “decimal”, title: “Trigger Outside(0)/Between(1) Threshold”, description: “Between(1)/Outside(0)”, defaultValue: 1, required: false, displayDuringSetup: true
      input “Param9”, “decimal”, title: “Periodic Reporting Interval. (Value x 10s)”, description: “Numbers 0 to 255 allowed.”, defaultValue: 30, required: false, displayDuringSetup: true
      input “Param11”, “decimal”, title: “Momentary Relay Enable (Value x 100ms)”, description: " Numbers 0 to 255 allowed.", defaultValue: 0, required: false, displayDuringSetup: true
      input “trigVoltage”, “decimal”, title: “Miltilevel Sensor Trigger Voltage”, description: “Numbers 0 to 16 allowed.”, defaultValue: 4, required: false, displayDuringSetup: true
      }

      // UI tile definitions
      tiles (scale: 2) {
      standardTile(“switch”, “device.switch”, width: 4, height: 4, canChangeIcon: false, decoration: “flat”) {
      state “on”, label: “On”, action: “off”, icon: “http://swiftlet.technology/wp-content/uploads/2016/06/Switch-On-104-edit.png”, backgroundColor: “#53a7c0"
      state “off”, label: ‘Off’, action: “on”, icon: “http://swiftlet.technology/wp-content/uploads/2016/06/Switch-Off-104-edit.png”, backgroundColor: “#ffffff
      }
      standardTile(“contact”, “device.contact”, width: 2, height: 2, inactiveLabel: false) {
      state “open”, label: ‘${name}’, icon: “st.contact.contact.open”, backgroundColor: “#ffa81e"
      state “closed”, label: ‘${name}’, icon: “st.contact.contact.closed”, backgroundColor: “#79b821
      }
      standardTile(“refresh”, “device.switch”, width: 2, height: 2, inactiveLabel: false, decoration: “flat”) {
      state “default”, label:’’, action:“refresh.refresh”, icon:“st.secondary.refresh”
      }
      standardTile(“powered”, “device.powered”, width: 2, height: 2, inactiveLabel: false) {
      state “powerOn”, label: “Power On”, icon: “st.switches.switch.on”, backgroundColor: “#79b821"
      state “powerOff”, label: “Power Off”, icon: “st.switches.switch.off”, backgroundColor: “#ffa81e
      }
      standardTile(“configure”, “device.configure”, width: 2, height: 2, inactiveLabel: false, decoration: “flat”) {
      state “configure”, label:’’, action:“configuration.configure”, icon:“st.secondary.configure”
      }
      valueTile(“voltage”, “device.voltage”, width: 2, height: 2) {
      state “val”, label:’${currentValue}v’, unit:””, defaultState: true
      }
      valueTile(“voltageCounts”, “device.voltageCounts”, width: 2, height: 2) {
      state “val”, label:’${currentValue}’, unit:”", defaultState: true
      }
      main ([“switch”])
      details([“switch”, “contact”, “voltage”, “powered”, “refresh”,“configure”])
      }
      }

    def parse(String description) {
    log.debug “description is: ${description}”

     def result = null
     def cmd = zwave.parse(description, [0x20: 1, 0x84: 1, 0x30: 1, 0x70: 1, 0x31: 5])
     
     log.debug "command value is: $cmd.CMD"
     
     if (cmd.CMD == "7105") {				//Mimo sent a power loss report
     	log.debug "Device lost power"
     	sendEvent(name: "powered", value: "powerOff", descriptionText: "$device.displayName lost power")
     } else {
     	sendEvent(name: "powered", value: "powerOn", descriptionText: "$device.displayName regained power")
     }
     //log.debug "${device.currentValue('contact')}" // debug message to make sure the contact tile is working
     if (cmd) {
     	log.debug "Creating Event :$cmd.CMD"
     	result = createEvent(zwaveEvent(cmd))
     }
     log.debug "Parse returned ${result?.descriptionText} $cmd.CMD"
     return result
    

    }

    def updated() {
    log.debug "Settings Updated…"
    configure()
    }

    def zwaveEvent(physicalgraph.zwave.commands.configurationv1.ConfigurationReport cmd) {
    log.debug “zwaveEvent ConfigurationReport: ‘${cmd}’”
    [:]
    }

    //notes about zwaveEvents:
    // these are special overloaded functions which MUST be returned with a map similar to (return [name: “switch”, value: “on”])
    // not doing so will produce a null on the parse function, this will mess you up in the future.
    // Perhaps can use ‘createEvent()’ and return that as long as a map is inside it.
    def zwaveEvent(physicalgraph.zwave.commands.switchbinaryv1.SwitchBinaryReport cmd) {
    log.debug "switchBinaryReport ${cmd}"
    if (cmd.value) // if the switch is on it will not be 0, so on = true
    {
    return [name: “switch”, value: “on”] // change switch value to on
    }
    else // if the switch sensor report says its off then do…
    {
    return [name: “switch”, value: “off”] // change switch value to off
    }

    }

    // working on next for the analogue and digital stuff.
    def zwaveEvent(physicalgraph.zwave.commands.basicv1.BasicSet cmd) // basic set is essentially our digital sensor for SIG1
    {
    log.debug “sent a BasicSet ${cmd}”
    //this may not be working, but not using this
    zwave.sensorMultilevelV5.sensorMultilevelGet().format()
    return [name: “powered”, value: “powerOn”]
    }

    def zwaveEvent(physicalgraph.zwave.commands.sensorbinaryv1.SensorBinaryReport cmd)
    {
    log.debug "sent a sensorBinaryReport ${cmd}"
    if (cmd.sensorValue) // if the switch is on it will not be 0, so on = true
    {
    return [name: “contact”, value: “open”] // change switch value to on
    }
    else // if the switch sensor report says its off then do…
    {
    return [name: “contact”, value: “closed”] // change switch value to off
    }
    }

    def zwaveEvent (physicalgraph.zwave.commands.sensormultilevelv5.SensorMultilevelReport cmd) // sensorMultilevelReport is used to report the value of the analog voltage for SIG1
    {
    log.debug "sent a SensorMultilevelReport"
    def ADCvalue = cmd.scaledSensorValue
    sendEvent(name: “voltageCounts”, value: ADCvalue)

     def mapp = CalculateVoltage(cmd.scaledSensorValue)
     
     if (mapp.value > trigVoltage)
     {
     	sendEvent(name: "voltage", value: mapp.value)
     	return [name: "contact", value: "open"]
     }
     if (mapp.value < trigVoltage)
     {
     	sendEvent(name: "voltage", value: mapp.value)
     	return [name: "contact", value: "closed"]
     }
    

    }

    def zwaveEvent(physicalgraph.zwave.Command cmd) {
    // Handles all Z-Wave commands we aren’t interested in
    log.debug(“Un-parsed Z-Wave message ${cmd}”)
    [:]
    }

    def CalculateVoltage(ADCvalue)
    {
    def map = [:]

      def volt = (((1.5338*(10**-16))*(ADCvalue**5)) - ((1.2630*(10**-12))*(ADCvalue**4)) + ((3.8111*(10**-9))*(ADCvalue**3)) - ((4.7739*(10**-6))*(ADCvalue**2)) + ((2.8558*(10**-3))*(ADCvalue)) - (2.2721*(10**-2)))
    
     //def volt = (((3.19*(10**-16))*(ADCvalue**5)) - ((2.18*(10**-12))*(ADCvalue**4)) + ((5.47*(10**-9))*(ADCvalue**3)) - ((5.68*(10**-6))*(ADCvalue**2)) + (0.0028*ADCvalue) - (0.0293))
     //log.debug "$cmd.scale $cmd.precision $cmd.size $cmd.sensorType $cmd.sensorValue $cmd.scaledSensorValue"
     def voltResult = volt.round(1)// + "v"    
     
     map.name = "voltage"
     map.value = voltResult
     map.unit = "v"
     return map
    

    }

    def configure() {
    def triggerMapping = Param3.toInteger()
    def lowerhigh = Param4.toInteger()
    def lowerlow = Param5.toInteger()
    def higherhigh = Param6.toInteger()
    def higherlow = Param7.toInteger()
    def inputFlags = (Param8_1.toInteger()*2) + Param8_0.toInteger()
    def periodicInterval = Param9.toInteger()
    def momentartRelay = Param11.toInteger()
    def cmds = []

     //Config Parameters
     if (Param2 == 1)
     {
     	log.debug "Param2:Clear"
     	cmds << zwave.configurationV1.configurationSet(parameterNumber: 2, size: 1, configurationValue:[1]).format() // clear pulse meter counts
     }    
     log.debug "Param3:$triggerMapping"
     cmds << zwave.configurationV1.configurationSet(parameterNumber: 3, size: 1, configurationValue:[triggerMapping]).format() // sig 1 triggers relay //no trigger
     log.debug "Param4:$lowerhigh"
     cmds << zwave.configurationV1.configurationSet(parameterNumber: 4, size: 1, configurationValue:[lowerhigh]).format() // lower threshold, high
     log.debug "Param5:$lowerlow"
     cmds << zwave.configurationV1.configurationSet(parameterNumber: 5, size: 1, configurationValue:[lowerlow]).format() // lower threshold, low
     log.debug "Param6:$higherhigh"
     cmds << zwave.configurationV1.configurationSet(parameterNumber: 6, size: 1, configurationValue:[higherhigh]).format() // upper threshold, high
     log.debug "Param7:$higherlow"
     cmds << zwave.configurationV1.configurationSet(parameterNumber: 7, size: 1, configurationValue:[higherlow]).format() // upper threshold, low
     log.debug "Param8:$inputFlags"
     cmds << zwave.configurationV1.configurationSet(parameterNumber: 8, size: 1, configurationValue:[inputFlags]).format() // set to analog, below bounds
     log.debug "Param9:$periodicInterval"
     cmds << zwave.configurationV1.configurationSet(parameterNumber: 9, size: 1, configurationValue:[periodicInterval]).format() // disable periodic reports //enable 10 sec
     log.debug "Param11:$momentartRelay"
     cmds << zwave.configurationV1.configurationSet(parameterNumber: 11, size: 1, configurationValue:[momentartRelay]).format() // momentary relay //no delay
     
     //Association Groups
     if (Group1 == 1) 
     {
     	log.debug "Group1:On" 
     	cmds << zwave.associationV1.associationSet(groupingIdentifier:1, nodeId:zwaveHubNodeId).format()	//subscribe to basic sets on sig1
     }
     else
     {
     	log.debug "Group1:Off"
     	cmds << zwave.associationV1.associationRemove(groupingIdentifier:1, nodeId:zwaveHubNodeId).format()
     }
     if (Group2 == 1) 
     {
     	log.debug "Group2:On"
     	cmds << zwave.associationV1.associationSet(groupingIdentifier:2, nodeId:zwaveHubNodeId).format()	//periodic multilevel reports on sig1
     }
     else
     {
     	log.debug "Group2:Off"
     	cmds << zwave.associationV1.associationRemove(groupingIdentifier:2, nodeId:zwaveHubNodeId).format()
     }
     if (Group3 == 1) 
     {
     	log.debug "Group3:On"
     	cmds << zwave.associationV1.associationSet(groupingIdentifier:3, nodeId:zwaveHubNodeId).format()	//power disconnect
     }
     else
     {
     	log.debug "Group3:Off"
     	cmds << zwave.associationV1.associationRemove(groupingIdentifier:3, nodeId:zwaveHubNodeId).format()	
     }
     if (Group4 == 1) 
     {
     	log.debug "Group4:On"
     	cmds << zwave.associationV1.associationSet(groupingIdentifier:4, nodeId:zwaveHubNodeId).format()	//subscribe to binary report on sig1
     }
     else
     {
     	log.debug "Group4:Off"
     	cmds << zwave.associationV1.associationRemove(groupingIdentifier:5, nodeId:zwaveHubNodeId).format()
     }
     if (Group5 == 1) 
     {
     	log.debug "Group5:On"
     	cmds << zwave.associationV1.associationSet(groupingIdentifier:5, nodeId:zwaveHubNodeId).format()	//subscribe to pulse counts on sig1
     }
     else
     {
     	log.debug "Group5:Off"
     	cmds << zwave.associationV1.associationRemove(groupingIdentifier:5, nodeId:zwaveHubNodeId).format()
     }
     
     log.debug "Requesting switch binary report"
     cmds << zwave.switchBinaryV1.switchBinaryGet().format()
     
     log.debug "Requesting multisensor report"
    	cmds << zwave.sensorMultilevelV5.sensorMultilevelGet().format()
    
     delayBetween(cmds, 500)
    

    }

    def on() {
    delayBetween([
    zwave.basicV1.basicSet(value: 0xFF).format(), // physically changes the relay from on to off and requests a report of the relay
    refresh()// to make sure that it changed (the report is used elsewhere, look for switchBinaryReport()
    ])
    }

    def off() {
    delayBetween([
    zwave.basicV1.basicSet(value: 0x00).format(), // physically changes the relay from on to off and requests a report of the relay
    refresh()// to make sure that it changed (the report is used elsewhere, look for switchBinaryReport()
    ])
    }

    def refresh() {
    log.debug “REFRESH!”

     def cmds = []    
     log.debug "Requesting multisensor report"
     cmds << zwave.switchBinaryV1.switchBinaryGet().format()
     cmds << zwave.sensorMultilevelV5.sensorMultilevelGet().format()
     delayBetween(cmds, 500)
    

    }

Update: (Adding battery backup without getting new battery)

  1. Instead of using the adapter that came with mimolite, I connected the power terminals of mimolite to battery serving as backup for security panel.
  2. Zone reporting worked as before
  3. But I had to disconnect the Sig+ from Bell+ and Sig- from Bell- due to load mismatch
  4. So I connected Sig+ to PGM2(programmable output of DSC PC1616 security panel) and programmed it to trigger Burglary and Fire the same way my PGM1 is programmed to report Burglary and Fire to my monitoring company.
    (I just asked my monitoring company what PGM1 was programmed to.) the SIG- doesnt need to be connected in this configuration.
    PROGRAM CMD: ( * 8 INSTALLER_CODE 009 01 01 # # )
  5. I changed Thresholds to match mew voltages. In this scenario, the PGM2 always remains at 2.6V and when triggered goes to 0 V So I set my trigger between 0V and 1.5V (0,0,79,109) These values can be programmed in UI in smartthings for mimolite if you use the above handler.

Thanks for sharing your code, I appreciate it. I am trying to make the Mimolite work with a float sensor that I have and output a % full. I was able to use what you’ve done here as a starting block and I can get everything to work other than flattening the scale since my sensor has a nonlinear curve to it.

Here is what it Reports for Voltage Counts:
At 100%, it Reports 3049
75% = 2911
50% = 2629
25% = 1903
0%. = 6

I see on line 196, you are flattening our a voltage curve as well I just don’t understand the formula or how to adjust it to another scale. If I can get it close, I’d like it to show the %. If I multiply the value calculated by 27.95, I get it as a % with 100 at the top and 0 at the bottom but when half full, it Reports about 75%, and at 1/4 full it Reports 40-50%.

I’m sure this is just a calculation fix and I thought I’d ask. It’s a tough one to reverse engineer and I am certainly no programmer.

Any chance you could help me out?

Hi,
Its good to see that some one found my post useful.
Sorry for late reply, but I was super busy this holiday season.
Sorry to say this but i used the same equation to calculate voltage that was in the 1st version of the handler that i based this on.
I can suggest you one method that may not be as efficient as writing a new equation, but you can try it …
After taking care of nonlinear curve, I get voltages in a range of 0 to V+. You can just use those voltages to map those voltages to %.
According to tech appendix, 3096 is about 3.8V so 0v == 0% and 3.8v == 100%
Do you see any issues with this approach.