Fortezz Water Meter

Hell all

I am new to the Hubitat community and home automation. I purchased a home that had some zwave products so I thought I would give it a shot. I was able to join and control some light switches which were on the prior owners system that he took with him.

My issue is this water meter. It is joined to the hubitat but does not seem to have any native drivers. How do I get this to report data. They do have install guides for ST but I am not sure how that translates or how to even complete that task.

If any one could point me in the right direction I would appreciate it.

I have no way to test this, but a quick port of the ST DTH may give you something to start with:

Fortezz Flow Meter Device Driver
/**
 *  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.
 *
 ********** Ported from the ST DTH ******************
 */
metadata {
	definition (name: "FortrezZ Flow Meter Interface", namespace: "fortrezz", author: "Daniel Kurin") {
		capability "Battery"
		capability "Energy Meter"
		capability "Image Capture"
		capability "Temperature Measurement"
        capability "Sensor"
        capability "Water Sensor"
        
        attribute "gpm", "number"
        attribute "cumulative", "number"
        attribute "alarmState", "string"
        attribute "chartMode", "string"
        attribute "lastThreshhold", "number"

        
        command "chartMode"
        command "zero"
        command "setHighFlowLevel", ["number"]

	    fingerprint deviceId: "0x2101", inClusters: "0x5E, 0x86, 0x72, 0x5A, 0x73, 0x71, 0x85, 0x59, 0x32, 0x31, 0x70, 0x80, 0x7A"
	}
   
    preferences {
        input ("version", "text", title: "Plugin Version 1.5", description:"", required: false, displayDuringSetup: true)
       input "gallonThreshhold", "decimal", title: "High Flow Rate Threshhold", description: "Flow rate (in gpm) that will trigger a notification.", defaultValue: 5, required: false, displayDuringSetup: true
    }

    
}

// parse events into attributes
def parse(String description) {
	def results = []
	if (description.startsWith("Err")) {
	    results << createEvent(descriptionText:description, displayed:true)
	} else {
		def cmd = zwave.parse(description, [ 0x80: 1, 0x84: 1, 0x71: 2, 0x72: 1 ])
		if (cmd) {
			results << createEvent( zwaveEvent(cmd) )
		}
	}
	//log.debug "\"$description\" parsed to ${results.inspect()}"
    if(gallonThreshhold != device.currentValue("lastThreshhold"))
    {
    	results << setThreshhold(gallonThreshhold)
    }
	log.debug "zwave parsed to ${results.inspect()}"
	return results
}

def updated()
{
	log.debug("Updated")
}

def setHighFlowLevel(level)
{
	setThreshhold(level)
}

def take() {
	def mode = device.currentValue("chartMode")
    if(mode == "day")
    {
    	take1()
    }
    else if(mode == "week")
    {
    	take7()
    }
    else if(mode == "month")
    {
    	take28()
    }
}

def chartMode(string) {
	log.debug("ChartMode")
	def state = device.currentValue("chartMode")
    def tempValue = ""
	switch(state)
    {
    	case "day":
        	tempValue = "week"
            break
        
        case "week":
        	tempValue = "month"
            break
            
        case "month":
        	tempValue = "day"
            break
            
        default:
        	tempValue = "day"
            break
    }
	sendEvent(name: "chartMode", value: tempValue)
    take()
}

def take1() {
    api("24hrs", "") {
        log.debug("Image captured")

        if(it.headers.'Content-Type'.contains("image/png")) {
            if(it.data) {
                storeImage(getPictureName("24hrs"), it.data)
            }
        }
    }
}

def take7() {
    api("7days", "") {
        log.debug("Image captured")

        if(it.headers.'Content-Type'.contains("image/png")) {
            if(it.data) {
                storeImage(getPictureName("7days"), it.data)
            }
        }
    }
}

def take28() {
    api("4weeks", "") {
        log.debug("Image captured")

        if(it.headers.'Content-Type'.contains("image/png")) {
            if(it.data) {
                storeImage(getPictureName("4weeks"), it.data)
            }
        }
    }
}

def zero()
{
	delayBetween([
		zwave.meterV3.meterReset().format(),
        zwave.meterV3.meterGet().format(),
        zwave.firmwareUpdateMdV2.firmwareMdGet().format(),
    ], 100)
}

def zwaveEvent(hubitat.zwave.commands.sensormultilevelv5.SensorMultilevelReport cmd)
{
	log.debug cmd
	def map = [:]
	if(cmd.sensorType == 1) {
		map = [name: "temperature"]
        if(cmd.scale == 0) {
        	map.value = getTemperature(cmd.scaledSensorValue)
        } else {
	        map.value = cmd.scaledSensorValue
        }
        map.unit = location.temperatureScale
	} /* else if(cmd.sensorType == 2) {
    	map = [name: "waterState"]
        if(cmd.sensorValue[0] == 0x80) {
        	map.value = "flow"
            sendEvent(name: "water", value: "dry")
        } else if(cmd.sensorValue[0] == 0x00) {
	        map.value = "none"
            sendEvent(name: "water", value: "dry")
        } else if(cmd.sensorValue[0] == 0xFF) {
	        map.value = "overflow"
            sendEvent(name: "water", value: "wet")
            sendAlarm("waterOverflow")
        }
	} */
	return map
}

def zwaveEvent(hubitat.zwave.commands.meterv3.MeterReport cmd)
{
	def map = [:]
    map.name = "gpm"
    def delta = cmd.scaledMeterValue - cmd.scaledPreviousMeterValue
    if (delta < 0 || delta > 10000) {
        log.error(cmd)
    	delta = 0
    }

    map.value = delta
    map.unit = "gpm"
    sendDataToCloud(delta)
    sendEvent(name: "cumulative", value: cmd.scaledMeterValue, displayed: false, unit: "gal")
	return map
}

def zwaveEvent(hubitat.zwave.commands.alarmv2.AlarmReport cmd)
{
	def map = [:]
    if (cmd.zwaveAlarmType == 8) // Power Alarm
    {
    	map.name = "powerState" // For Tile (shows in "Recently")
        if (cmd.zwaveAlarmEvent == 2) // AC Mains Disconnected
        {
            map.value = "disconnected"
            sendAlarm("acMainsDisconnected")
        }
        else if (cmd.zwaveAlarmEvent == 3) // AC Mains Reconnected
        {
            map.value = "reconnected"
            sendAlarm("acMainsReconnected")
        }
        else if (cmd.zwaveAlarmEvent == 0x0B) // Replace Battery Now
        {
            map.value = "noBattery"
            sendAlarm("replaceBatteryNow")
        }
        else if (cmd.zwaveAlarmEvent == 0x00) // Battery Replaced
        {
            map.value = "batteryReplaced"
            sendAlarm("batteryReplaced")
        }
    }
    else if (cmd.zwaveAlarmType == 4) // Heat Alarm
    {
    	map.name = "heatState"
        if (cmd.zwaveAlarmEvent == 0) // Normal
        {
            map.value = "normal"
        }
        else if (cmd.zwaveAlarmEvent == 1) // Overheat
        {
            map.value = "overheated"
            sendAlarm("tempOverheated")
        }
        else if (cmd.zwaveAlarmEvent == 5) // Underheat
        {
            map.value = "freezing"
            sendAlarm("tempFreezing")
        }
    }
    else if (cmd.zwaveAlarmType == 5) // Water Alarm
    {
    	map.name = "waterState"
        if (cmd.zwaveAlarmEvent == 0) // Normal
        {
            map.value = "none"
            sendEvent(name: "water", value: "dry")
        }
        else if (cmd.zwaveAlarmEvent == 6) // Flow Detected
        {
        	if(cmd.eventParameter[0] == 2)
            {
                map.value = "flow"
                sendEvent(name: "water", value: "dry")
            }
            else if(cmd.eventParameter[0] == 3)
            {
            	map.value = "overflow"
                sendAlarm("waterOverflow")
                sendEvent(name: "water", value: "wet")
            }
        }
    }
    //log.debug "alarmV2: $cmd"
    
	return map
}

def zwaveEvent(hubitat.zwave.commands.batteryv1.BatteryReport cmd) {
	def map = [:]
	if(cmd.batteryLevel == 0xFF) {
		map.name = "battery"
		map.value = 1
		map.descriptionText = "${device.displayName} has a low battery"
		map.displayed = true
	} else {
		map.name = "battery"
		map.value = cmd.batteryLevel > 0 ? cmd.batteryLevel.toString() : 1
		map.unit = "%"
		map.displayed = false
	}
	return map
}

def zwaveEvent(hubitat.zwave.Command cmd)
{
	log.debug "COMMAND CLASS: $cmd"
}

def sendDataToCloud(double data)
{
    def params = [
        uri: "https://iot.swiftlet.technology",
        path: "/fortrezz/post.php",
        body: [
            id: device.id,
            value: data,
            email: registerEmail
        ]
    ]

	//log.debug("POST parameters: ${params}")
    try {
        httpPostJson(params) { resp ->
            resp.headers.each {
                //log.debug "${it.name} : ${it.value}"
            }
            log.debug "sendDataToCloud query response: ${resp.data}"
        }
    } catch (e) {
        log.debug "something went wrong: $e"
    }
}

def getTemperature(value) {
	if(location.temperatureScale == "C"){
		return value
    } else {
        return Math.round(celsiusToFahrenheit(value))
    }
}

private getPictureName(category) {
  //def pictureUuid = device.id.toString().replaceAll('-', '')
  def pictureUuid = java.util.UUID.randomUUID().toString().replaceAll('-', '')

  def name = "image" + "_$pictureUuid" + "_" + category + ".png"
  return name
}

def api(method, args = [], success = {}) {
  def methods = [
    //"snapshot":        [uri: "http://${ip}:${port}/snapshot.cgi${login()}&${args}",        type: "post"],
    "24hrs":      [uri: "https://iot.swiftlet.technology/fortrezz/chart.php?uuid=${device.id}&tz=${location.timeZone.ID}&type=1", type: "get"],
    "7days":      [uri: "https://iot.swiftlet.technology/fortrezz/chart.php?uuid=${device.id}&tz=${location.timeZone.ID}&type=2", type: "get"],
    "4weeks":     [uri: "https://iot.swiftlet.technology/fortrezz/chart.php?uuid=${device.id}&tz=${location.timeZone.ID}&type=3", type: "get"],
  ]

  def request = methods.getAt(method)

  return doRequest(request.uri, request.type, success)
}

private doRequest(uri, type, success) {
  log.debug(uri)

  if(type == "post") {
    httpPost(uri , "", success)
  }

  else if(type == "get") {
    httpGet(uri, success)
  }
}

def sendAlarm(text)
{
	sendEvent(name: "alarmState", value: text, descriptionText: text, displayed: false)
}

def setThreshhold(rate)
{
	log.debug "Setting Threshhold to ${rate}"
    
    def event = createEvent(name: "lastThreshhold", value: rate, displayed: false)
    def cmds = []
    cmds << zwave.configurationV2.configurationSet(configurationValue: [(int)Math.round(rate*10)], parameterNumber: 5, size: 1).format()
    sendEvent(event)
    return response(cmds) // return a list containing the event and the result of response()
}

Welcome to the Hubitat community!

Interesting typo.

Because you are new to Hubitat, here is how to install a custom driver:

https://docs.hubitat.com/index.php?title=How_to_Install_Custom_Drivers

1 Like

The learning curve is a little hellish but I am sure I will get there. Thanks for the info.

1 Like

The ST Fortrezz driver mentioned above is for an earlier 2016 ST version and now loads as an energy meter. I unfortunately also purchased this Fortrezz Flow Meter and have not been able to get functionality with either a ST or Hubitat hub. I would appreciate help from anyone that can get flow reporting functionality from this meter to create safety routines to shut water off when leaks exist.

1 Like