Thermostat modes not available in rule machine

Hi, im using Generic z-wave plus Thermostat driver to control an Aidoo module on my Mitsubishi mini split. I wanted to write a rule to set the mode to cool or heat, but the modes dont seem to be populating in the dropdown in rule machine. When I click the drop down, there are no options, just click to set again. I should be seeing cool, heat, auto or off there, no?

I would look at the device page for the Thermostat. I believe that dropdown should be populated from the supportedThermostatModes attribute, as different thermostats have unique modes available.

image

If they are not there, maybe try pressing configure or initialize to get it to populate. Otherwise, maybe change the driver to Virtual Thermostat which has a command to set the map, and then change it back to the Generic Zwave driver after.

1 Like

Brilliant! Using the virtual thermostat driver to set the maps seems to have worked, thank you! The z-wave implementation of this module is a bit sparse and non-standard. Ive contacted them about implementing Operational State, but not getting anywhere with them yet. There must be a ton of Mitsubishi mini-splits out there, im shocked it's not better supported.

These are mode jsons I ended up setting and they seem to work.

It looks like maybe your driver came from here?

That driver is missing the thermostatOperatingState attribute. It is trying to send an enum event for data.on.

image

On their local API doc, that looks like a 0 or 1 binary value, for operating or not.
image

I changed it to this:


and added the attribute:
image

If you want to try it out.

metadata {
    definition(name: "Airzone Aidoo Pro (Local API)", namespace: "my hubitat", author: "AP'") {
        capability "TemperatureMeasurement"
        capability "RelativeHumidityMeasurement"
        capability "Thermostat"
        capability "Refresh"
        capability "Polling"

        attribute "connectivity", "string"
        attribute "ecoMode", "string"
        attribute "ecoCoolPoint", "number"
        attribute "ecoHeatPoint", "number"
        attribute "roomTemp", "number"
        attribute "zoneID", "number"
        attribute "systemID", "number"
        attribute "modeText", "string"
        attribute "fanModeText", "string"
        attribute "supportedThermostatModes", "JSON_OBJECT"
        attribute "supportedThermostatFanModes", "JSON_OBJECT"
        attribute "thermostatOperatingState", "ENUM"

        command "setThermostatMode", [[name:"Thermostat mode*", type:"ENUM", constraints:["off","heat","cool","auto"]]]
        command "setThermostatFanMode", [[name:"Fan mode*", type:"ENUM", constraints:["auto","on"]]]
        command "setHeatingSetpoint", [[name:"Heating setpoint*", type:"NUMBER", description:"Set Heating Point (°C)"]]
        command "setCoolingSetpoint", [[name:"Cooling setpoint*", type:"NUMBER", description:"Set Cooling Point (°C)"]]
        command "refresh"
    }
    preferences {
        input("ip", "string", title:"Aidoo IP Address", description:"Device local IP, e.g. 192.168.1.73", required: true)
        input("systemID", "number", title:"System ID", description:"Usually 1", defaultValue:1, required: true)
        input("zoneID", "number", title:"Zone ID", description:"Usually 1", defaultValue:1, required: true)
        input("pollInterval", "number", title:"Polling interval (mins)", defaultValue:2, required: false)
    }
}

def installed() { initialize() }
def updated() { unschedule(); initialize() }
def initialize() {
    state.lastPoll = now()
    if (settings.pollInterval) runEveryNMinutes("refresh", settings.pollInterval as int)
    refresh()
}

def setThermostatAttributes() {
	sendEvent(name: "thermostatOperatingState", value: "off")
	sendEvent(name: "supportedThermostatFanModes", value: '["low","medium","high","auto","disable"]')
	sendEvent(name: "supportedThermostatModes", value: '["heat","cool","idle"]')
	sendEvent(name: "thermostatFanMode", value: "auto")
	sendEvent(name: "thermostatMode", value: "idle")
}

// Main polling function - pulls latest zone/system state
def refresh() {
    def apiUrl = "http://${settings.ip}:3000/api/v1/hvac"
    def body = [systemID: settings.systemID, zoneID: settings.zoneID]
    def params = [
        uri: apiUrl,
        contentType: "application/json",
        body: groovy.json.JsonOutput.toJson(body),
        timeout: 5
    ]
    try {
        httpPost(params) { resp ->
            if(resp.status == 200 && resp.data?.data) {
                parseApiResponse(resp.data.data[0])
                sendEvent(name: "connectivity", value: "ONLINE")
            } else {
                sendEvent(name: "connectivity", value: "OFFLINE")
            }
        }
    } catch (e) {
        log.warn "Airzone API error: $e"
        sendEvent(name: "connectivity", value: "OFFLINE")
    }
}

def parseApiResponse(data) {
    // Standard states
    sendEvent(name: "temperature", value: data.roomTemp ?: data.setpoint)
    sendEvent(name: "humidity", value: data.humidity ?: 0)
    sendEvent(name: "heatingSetpoint", value: data.heatsetpoint ?: 0)
    sendEvent(name: "coolingSetpoint", value: data.coolsetpoint ?: 0)
    sendEvent(name: "thermostatMode", value: modeMap(data.mode))
    if (data.on == 1) sendEvent(name: "thermostatOperatingState", value: "heating")
    if (data.on == 0) sendEvent(name: "thermostatOperatingState", value: "idle")
    //sendEvent(name: "thermostatOperatingState", value: data.on ? "heating" : "idle")
    sendEvent(name: "thermostatFanMode", value: fanModeMap(data.speed))

    // Update available modes for drop-downs (always in sync)
    sendEvent(name: "supportedThermostatModes", value: groovy.json.JsonOutput.toJson(["off","heat","cool","auto"]))
    sendEvent(name: "supportedThermostatFanModes", value: groovy.json.JsonOutput.toJson(["auto","on"]))

    // Custom states
    sendEvent(name: "ecoMode", value: data.eco_mode ? "ON" : "OFF")
    sendEvent(name: "ecoCoolPoint", value: data.ecoCoolPoint)
    sendEvent(name: "ecoHeatPoint", value: data.ecoHeatPoint)
    sendEvent(name: "roomTemp", value: data.roomTemp)
    sendEvent(name: "zoneID", value: data.zoneID)
    sendEvent(name: "systemID", value: data.systemID)
    sendEvent(name: "modeText", value: modeMap(data.mode))
    sendEvent(name: "fanModeText", value: fanModeMap(data.speed))
}

def modeMap(n) {
    switch(n as int) {
        case 1: return "heat"
        case 2: return "cool"
        case 3: return "auto"
        case 4: return "off"
        default: return "off"
    }
}
def fanModeMap(n) {
    switch(n as int) {
        case 0: return "auto"
        case 1: return "on"
        default: return "auto"
    }
}

// Set Thermostat Mode
def setThermostatMode(String mode) {
    def modeVal = modeStrToApi(mode)
    sendEvent(name: "thermostatMode", value: mode)   // <-- UI stays in sync immediately
    sendPut([systemID: settings.systemID, zoneID: settings.zoneID, mode: modeVal])
}
def setThermostatFanMode(String fanMode) {
    def speedVal = (fanMode == "on") ? 1 : 0
    sendEvent(name: "thermostatFanMode", value: fanMode)
    sendPut([systemID: settings.systemID, zoneID: settings.zoneID, speed: speedVal])
}
def setHeatingSetpoint(setpoint) {
    sendPut([systemID: settings.systemID, zoneID: settings.zoneID, heatsetpoint: setpoint as int])
}
def setCoolingSetpoint(setpoint) {
    sendPut([systemID: settings.systemID, zoneID: settings.zoneID, coolsetpoint: setpoint as int])
}
def sendPut(bodyMap) {
    def apiUrl = "http://${settings.ip}:3000/api/v1/hvac"
    def params = [
        uri: apiUrl,
        contentType: "application/json",
        body: groovy.json.JsonOutput.toJson(bodyMap),
        timeout: 5
    ]
    try {
        httpPut(params) { resp ->
            if(resp.status == 200) {
                log.info "Set succeeded: ${resp.data}"
                refresh()
            } else {
                log.warn "Set failed: ${resp.data}"
            }
        }
    } catch (e) {
        log.warn "Put error: $e"
    }
}
def modeStrToApi(mode) {
    switch(mode) {
        case "heat": return 1
        case "cool": return 2
        case "auto": return 3
        case "off": return 0
        default: return 0
    }
}
1 Like

All great input. However im using the Z-wave version of the Aidoo module. this driver would be for the Pro version of their wifi model that offers up the local API.

Yup, I should have reread your post. No biggie, it was funny that one also had an issue with operatingState. Do you have the Zwave Driver Code?

Im just using the Generic z-wave plus thermostat driver. They have an integration manual for their z-wave plus modules, like I said, it's a bit sparse. The only item they feed back to the controller seems to be room temperature. I'll post here if I have any luck getting them to add operating state. Im pretty sure it is available over the modbus from the unit, I seem to remember it was available on the MEL Kumo interface I was using before they broke everything with their Comfort app.