Cistern Drinking Water Level Indication - MIMOLITE?

Hi All,

I have a 50 cubic meter Cistern (13,000Gallons) at our acerage and I would like to monitor the tank level %.

I have a Fortrezz MIMOlite which I have used the existing coding out there to get the unit to display Voltage.

The mimolite unit displays 0-5VDC. I have installed a level transmitter that is ranged 0-2.5meters for tank level.

First I was hoping to change the readout so that 0 volts = 0 meters and 5 volts = 2.5 Meters.

Second I was hoping to have a second display that calculates volume of a horizontal capsule based off the tank level, I realize this would take a lot of calculations as the volume is non-linear. The tank is 30ft long by 8ft tall and 8ft wide.

My background is in industrial automation so I’m unfamiliar with how to write the code for this.

Any help would be great, Thanks!


Below I have copied the existing code im using:


  • 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:

  • 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", 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


// 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: "", backgroundColor: "#53a7c0"

		state "off", label: 'Off', action: "on", icon: "", backgroundColor: "#ffffff"


    standardTile("contact", "", width: 2, height: 2, inactiveLabel: false) {

		state "open", label: '${name}', icon: "", backgroundColor: "#ffa81e"

		state "closed", label: '${name}', icon: "", 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: "", 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 (["voltage"])

	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) {

	result = createEvent(zwaveEvent(cmd))


log.debug "Parse returned ${result?.descriptionText} $cmd.CMD"

return result


def updated() {

log.debug "Settings Updated..."



//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 command"


delayBetween([zwave.sensorMultilevelV5.sensorMultilevelGet().format()])// requests a report of the anologue input voltage

[name: "contact", value: cmd.value ? "open" : "closed"]}

//[name: "contact", value: cmd.value ? "open" : "closed", type: "digital"]}

def zwaveEvent(physicalgraph.zwave.commands.sensorbinaryv1.SensorBinaryReport cmd)


log.debug "sent a sensorBinaryReport command"


[name: "contact", value: cmd.value ? "open" : "closed"]


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 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" = "voltage"

map.value = voltResult

map.unit = "volts"

return map


def configure() {

def x = (RelaySwitchDelay*10).toInteger()

log.debug "Configuring.... " //setting up to monitor power alarm and actuator duration


	zwave.associationV1.associationSet(groupingIdentifier:3, nodeId:[zwaveHubNodeId]).format(), // 	FYI: Group 3: If a power dropout occurs, the MIMOlite will send an Alarm Command Class report 

    																							//	(if there is enough available residual power)

    zwave.associationV1.associationSet(groupingIdentifier:2, nodeId:[zwaveHubNodeId]).format(), // periodically send a multilevel sensor report of the ADC analog voltage to the input

    zwave.associationV1.associationSet(groupingIdentifier:4, nodeId:[zwaveHubNodeId]).format(), // when the input is digitally triggered or untriggered, snd a binary sensor report

    zwave.configurationV1.configurationSet(configurationValue: [x], parameterNumber: 11, size: 1).format() // configurationValue for parameterNumber means how many 100ms do you want the relay

    																										// to wait before it cycles again / size should just be 1 (for 1 byte.)

    //zwave.configurationV1.configurationGet(parameterNumber: 11).format() // gets the new parameter changes. not currently needed. (forces a null return value without a zwaveEvent funciton



def on() {


	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() {


	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!”


    zwave.switchBinaryV1.switchBinaryGet().format(), //requests a report of the relay to make sure that it changed (the report is used elsewhere, look for switchBinaryReport()

    zwave.sensorMultilevelV5.sensorMultilevelGet().format()// requests a report of the anologue input voltage