Virtual Sensor Device?

I’ve got a device which publishes

Humidity (numeric)
Temperature (numeric)
Pressure (numeric)
Indoor Air Quality Value (numeric value)
Calibration Status (numeric value 0-3)

This is from a BME680 sensor that I’ve got publishing to an mqtt broker and using node-red to move values in to Hubitat.

What I’d like to do is create a virtual device which I can use to containerise these values.

What’s the best place to start with this? Can I modify the virtual omnisensor - is there source code for this available?

Thanks!

Here you go:

1 Like

I would suggest that you remove those capabilities and commands you don't need, and add any that you need which aren't there.

1 Like

Darn, I have never noticed the code for the Omni Sensor was publicly available as an example. I have been using it for devices that just had temperature, lux, and humidity. Now I can get rid of extraneous capabilities and commands. Thank you.

1 Like

It wasn't. I just put that in the repo.

7 Likes

Awesome thanks !

The sensor returns an Internal Air Quality as a numeric value.. using grafana I’ve mapped ranges to a description of air quality e.g. “Good”, “Moderate” etc. How do you think I should best implement this within this virtual device?

1 Like

We don't have a capability yet for Air Quality. We plan to add one. So for now, you could add a custom attribute and command that sets it.

OK, I think I’ve cracked it.. does the below look ok?

metadata {
    definition (name: "Virtual BME680", namespace: "hubitat", author: "Daniel Edge") {
        capability "Carbon Dioxide Measurement"
        capability "Illuminance Measurement"
        capability "Relative Humidity Measurement"
        capability "Temperature Measurement"
        capability "Pressure Measurement"
        command "setCarbonDioxide", ["Number"]
        command "setIlluminance", ["Number"]
        command "setRelativeHumidity", ["Number"]
        command "setTemperature", ["Number"]
        command "setPressure", ["Number"]
        command "setIAQ", ["Number"]
        command "setCalibrationStatus", ["Number"]
		  attribute "IAQ", "Number"
		  attribute "Calibration", "Number"
    }
    preferences {
        input name: "logEnable", type: "bool", title: "Enable debug logging", defaultValue: true
        input name: "txtEnable", type: "bool", title: "Enable descriptionText logging", defaultValue: true
    }
}

def logsOff(){
    log.warn "debug logging disabled..."
    device.updateSetting("logEnable",[value:"false",type:"bool"])
}

def installed() {
    log.warn "installed..."
   /* arrived()
    accelerationInactive()
    COClear()
    close()
    setIlluminance(50)
    setCarbonDioxide(350)
    setRelativeHumidity(35)
    motionInactive()
    smokeClear()
    setTemperature(70)
    dry()*/
    runIn(1800,logsOff)
}

def updated() {
    log.info "updated..."
    log.warn "debug logging is: ${logEnable == true}"
    log.warn "description logging is: ${txtEnable == true}"
    if (logEnable) runIn(1800,logsOff)
}

def parse(String description) {
}





def setIlluminance(lux) {
    def descriptionText = "${device.displayName} is ${lux} lux"
    if (txtEnable) log.info "${descriptionText}"
    sendEvent(name: "illuminance", value: lux, descriptionText: descriptionText, unit: "Lux")
}

def setCarbonDioxide(CO2) {
    def descriptionText = "${device.displayName}  Carbon Dioxide is ${CO2} ppm"
    if (txtEnable) log.info "${descriptionText}"
    sendEvent(name: "carbonDioxide", value: CO2, descriptionText: descriptionText, unit: "ppm")
}

def setRelativeHumidity(humid) {
    def descriptionText = "${device.displayName} is ${humid}% humidity"
    if (txtEnable) log.info "${descriptionText}"
    sendEvent(name: "humidity", value: humid, descriptionText: descriptionText, unit: "RH%")
}

def setTemperature(temp) {
    def unit = "°${location.temperatureScale}"
    def descriptionText = "${device.displayName} is ${temp}${unit}"
    if (txtEnable) log.info "${descriptionText}"
    sendEvent(name: "temperature", value: temp, descriptionText: descriptionText, unit: unit)
}

def setPressure(Pressure) {
    def descriptionText = "${device.displayName}  Pressure is ${Pressure} ohms"
    if (txtEnable) log.info "${descriptionText}"
    sendEvent(name: "Pressure", value: Pressure, descriptionText: descriptionText, unit: "mbar")
}

def setIAQ(IAQ) {
    def descriptionText = "${device.displayName}  IAQ is ${IAQ}"
    if (txtEnable) log.info "${descriptionText}"
    sendEvent(name: "IAQ", value: IAQ, descriptionText: descriptionText, unit: "IAQ")
}

def setCalibrationStatus(CalStatus) {
    def descriptionText = "${device.displayName}  CalStatus is ${CalStatus}"
    if (txtEnable) log.info "${descriptionText}"
    sendEvent(name: "Calibration", value: CalStatus, descriptionText: descriptionText, unit: "CalStatus")
}

It may not matter but the BME680 sensor doesn't measure illuminance.

Are you using the Bosch Arduino driver? Have you found the air quality measurements to be useful? I had played around with the BME680 a year or so ago. It seemed to me the air quality sensor did not have a stable offset so you needed to expose it to "fresh" air periodically.

+1. I love this device as it's so versatile.

thanks for making it available @bravenel !

Thanks John - I kept illuminance in as I was toying with making it a virtual device for the entire device I’m building and left it in there

Early days on testing but I’m using the closed source official BSEC library.. once the sensor has self calibrated; I do get “meaningful” readings in so far as if I’m 3d printing something and doors windows closed air quality degrades; air quality improves again when window open - not the most scientific testing but it does seem to reflect a change in air quality from very preliminary observations

1 Like

My issue with the BEC680 (even using the BSEC code) is the readings are relative. If I put the sensor in a "bad" environment for a long length of time, it would eventually appear as good. As there is no change to form a relative reading of a good environment.
Said in another way:
Their algorithm with some filtering and logic simply assumes the "best" quality air it experiences is deemed as good.

In instrumentation terms, the transfer function "slope" is stable but the "offset" is all over the place.