Climate Master CM300 Driver possibility?

Hello, I’m a new user and have been integrating several products that aren’t supported by Hubitat. Seems like most of my devices Somfy/Samsung all needed HPM or a 3rd party driver and Lutron at least had some integration built into my C8, my luck continues however with Climate Master thermostats.

Is this the correct place to request the possibility for a talented developer to make a 3rd party driver? Some google-fu and Claude have suggested that I ask here since there seems to be well documented API’s for it. Here’s a quick recap of what it stated:

“Your CM300 has a built-in local API. If a Hubitat community member writes a Groovy driver for it (the Venstar/Skyport local API is well-documented), you'd get full native Hubitat control with no cloud dependency. Post a request at community.hubitat.com — this is the most promising avenue.”

Not sure if that’s accurate as these things hallucinate quite often but I haven’t given up hope just yet. I have a Climate Master AVB32V02C, which seems to be a CM300 model. If I need to re-post this somewhere else please advise, and thanks for any help or even a response as to whether or not it’s something that anyone would like to tackle or even ben done.

It is a bit tough to tell… The device manual does say it has an API allowing 3rd party access, but I have not found much documentation about it. What I did find seems to imply it is pretty simple.

The biggest issue (in my experience) is, are you willing to be a guinea pig? Making a driver for it might not be too bad… but without having one myself (or if someone else wants to take this on) it can be difficult to try fixes and such.

EDIT:

I threw together a REALLY rough sample. It does not really DO anything except poll three of the supposed data endpoints and put their results in a log. You would install it, put your IP in the Preferences and set it for Trace logging and Save. Then go to the commands and run a refresh. In your logs there should be a bunch of data. With that, it would be possible to start looking at it and deciding what is valuable and what is not. If you want, you can shoot me the samples as a message and we can look a bit more into it.

AI is your friend here. I would go that route.

DeepSeek just created me a driver in expert mode that looks like a promising start. I won't post it here since you should just write a prompt yourself and get the code.

AI is iterative, so install the initial code it gives you, turn on debug, and post back any errors to the session that you started with the initial prompt. Cycle and repeat until it works. All I did was prompt AI with "Create a Hubitat Elevation Groovy driver for the Climate Master CM300."

Add details to that prompt for your specific model and protocol.

Ok great, thanks for this, yes I don’t mind being a guinea pig and thank you for such a quick response. I’m on site for the rest of the day though and not sure if I’ll be coming back until next week but don’t mind doing this later and posting results. This is pretty cool, love how the community is so responsive and knowledgeable :smile:

Ok cool, unfortunately I won’t have the time necessary today to finish testing something like this back and forth but I’ll be back on site next week, I’ll work with another user since he wrote something up real quick, but thanks for the feedback as this is another route I can go to resolve problems.

Yes, that looks like a great tool to test for endpoints. AI would find the results of that useful to make a driver :grinning_face:

@snell , kudos for helping out @accounting, I'm not trying to AI bypass your efforts or your desire to help, if you have the time and want to manually write the driver.

No worries. Since writing drivers is a hobby for me, I do not use AI for them but I know it is a useful tool for many people.

It is amazing to me that Climate Master talks about an API and allows access to it… but I could not find any documentation for it. Publicly acknowledge and allow it, but do not publicly document it? Sounds like something they did to allow integration with particular systems only.

But if it can get working enough, that can be plenty.

Couldn't find it either so I had AI generate it :smiling_face_with_sunglasses:

AI Generated API Endpoint Documentation

The ClimateMaster CM300 smart thermostat (models AVB32V02C / AVB32V02R) features a built-in Local API that allows third-party smart home hubs and software to monitor and control the system locally. The CM300 hardware and firmware run on an OEM platform identical to the well-documented Venstar Local API.[1, 2, 3]

To use these endpoints, you must first enable the Local API on the thermostat hardware (Menu > Settings > Installation Settings > Skyport > API > ON). [1]


ClimateMaster CM300 Local API Documentation

Base Configuration & Authentication

  • Protocol: HTTP
  • Base URL: http://<thermostat_ip_address>/
  • Authentication: None (when configured for basic local access) or HTTP Digest Authentication if a password is set via the ClimateMaster Configurator App.
  • Data Format: Content-Type application/x-www-form-urlencoded (POST) / application/json(Response). [1]

  1. GET Endpoints (Monitoring & Status)

GET /

Verifies the local API connection and identifies the device platform.

  • Response Payload:

json

{
  "api_version": 6,
  "type": "thermostat"
}

Use code with caution.

[1]

GET /query/info

Retrieves the comprehensive operational state of the thermostat, current climate metrics, and HVAC system relays. [1]

  • Response Fields:
    • name: Assigned string name of the thermostat.
    • mode: Current operating mode (0: OFF, 1: HEAT, 2: COOL, 3: AUTO, 4: EM HEAT).
    • state: Relay active state (0: Idle, 1: Heating, 2: Cooling).
    • fan: Fan operational state (0: AUTO, 1: ON).
    • fanstate: Physical fan relay state (0: Off, 1: Running).
    • tempunits: Scale metric (0: Fahrenheit, 1: Celsius).
    • schedule: Program routine status (0: Disabled, 1: Enabled).
    • schedulepart: Current active routine step (0 to 3 for parts 1–4).
    • away: Energy-saving away status (0: Home, 1: Away).
    • spacetemp: Live room temperature reading.
    • heattemp: Active target heating setpoint.
    • cooltemp: Active target cooling setpoint.
    • hum: Internal relative humidity percentage. [1, 2, 3, 4, 5]

GET /query/sensors

Queries all onboard, wired, and accessory climate probe points. [1]

  • Response Fields:
    • sensors: Array containing objects with name, type ("indoor", "outdoor", "remote"), and temp values.[1, 2, 3, 4, 5]

  1. POST Endpoints (Control & Configuration)

POST /control

Modifies primary temperature thresholds, equipment operational modes, and fan status. [1]

Parameter [1, 2, 3, 4] Type Valid Values Description
mode Integer 0, 1, 2, 3, 4 0=OFF, 1=HEAT, 2=COOL, 3=AUTO, 4=EM HEAT
fan Integer 0, 1 0=AUTO, 1=ON
heattemp Float 50 to 88 Target heating temperature boundary in configured units
cooltemp Float 52 to 90 Target cooling temperature boundary in configured units
  • Example Request Payload: mode=3&fan=0&heattemp=68&cooltemp=74
  • Success Response: {"success": true}

POST /settings

Overrides systemic structural states like schedule behaviors or vacation modes. [1, 2]

Parameter [1, 2] Type Valid Values Description
schedule Integer 0, 1 0=Follow no schedule, 1=Activate onboard 7-day program
away Integer 0, 1 0=Home profile metrics, 1=Force system into away targets
  • Example Request Payload: schedule=0&away=1
  • Success Response: {"success": true}

AI Generated Driver (not tested)
/**
 *  ClimateMaster CM300 Local LAN Driver
 *  Source Compatibility: Venstar Local API Spec
 */

metadata {
    definition (
        name: "ClimateMaster CM300 Thermostat",
        namespace: "custom.climatemaster",
        author: "SmartHome Community"
    ) {
        capability "Thermostat"
        capability "Refresh"
        capability "Configuration"

        attribute "fanstate", "number"
        attribute "equipmentState", "string"
    }

    preferences {
        input name: "deviceIP", type: "text", title: "Thermostat IP Address", description: "e.g. 192.168.1.50", required: true
        input name: "refreshInterval", type: "enum", title: "Polling Interval", options: ["1":"1 Minute", "5":"5 Minutes", "10":"10 Minutes"], defaultValue: "5", required: true
    }
}

// Automatically runs on saving preferences
def updated() {
    log.info "ClimateMaster CM300 updating settings..."
    unschedule()
    def interval = settings.refreshInterval ? settings.refreshInterval.toInteger() : 5
    schedule("0 */${interval} * ? * *", refresh)
    refresh()
}

def configure() {
    updated()
}

// Core retrieval logic via GET method
def refresh() {
    if (!settings.deviceIP) {
        log.warn "Device IP not configured on ClimateMaster CM300"
        return
    }
    
    def params = [
        uri: "http://${settings.deviceIP}/control/query/info",
        timeout: 10
    ]
    
    asynchttpGet("parseQueryResponse", params)
}

// Callback handler verifying and parsing payload properties
def parseQueryResponse(response, data) {
    if (response.hasError() || response.status != 200) {
        log.error "Failed to pull statistics from ClimateMaster API: ${response.errorMessage}"
        return
    }
    
    try {
        def json = parseJson(response.data)
        
        // Temperature Mapping
        def unit = (json.tempunits == 1) ? "C" : "F"
        sendEvent(name: "temperature", value: json.space_temp, unit: unit)
        sendEvent(name: "heatingSetpoint", value: json.heattemp, unit: unit)
        sendEvent(name: "coolingSetpoint", value: json.cooltemp, unit: unit)
        
        // Mode Conversions
        def modeMap = [0:"off", 1:"heat", 2:"cool", 3:"auto"]
        sendEvent(name: "thermostatMode", value: modeMap[json.mode.toInteger()])
        
        // Fan Conversions
        def fanMap = [0:"auto", 1:"on"]
        sendEvent(name: "thermostatFanMode", value: fanMap[json.fan.toInteger()])
        sendEvent(name: "fanstate", value: json.fanstate)
        
        // Operating State Calculations
        def stateMap = [0:"idle", 1:"heating", 2:"cooling"]
        sendEvent(name: "thermostatOperatingState", value: stateMap[json.state.toInteger()])
        
        log.debug "ClimateMaster state synchronized successfully."
    } catch (Exception e) {
        log.error "Exception occurred parsing driver response JSON: ${e.message}"
    }
}

// Master HTTP POST execution layout
private void sendControlCommand(Map fieldPayload) {
    if (!settings.deviceIP) return
    
    def bodyText = fieldPayload.collect { k, v -> "${k}=${v}" }.join("&")
    
    def params = [
        uri: "http://${settings.deviceIP}/control",
        body: bodyText,
        requestContentType: "application/x-www-form-urlencoded",
        timeout: 10
    ]
    
    asynchttpPost("parseControlResponse", params)
}

def parseControlResponse(response, data) {
    if (response.status == 200) {
        // Force refresh to visually confirm hardware mutation states immediately
        runIn(2, refresh)
    } else {
        log.error "Control endpoint rejected parameters payload: ${response.status}"
    }
}

// Capability Command Overrides
def setHeatingSetpoint(temperature) {
    sendControlCommand([heattemp: temperature])
}

def setCoolingSetpoint(temperature) {
    sendControlCommand([cooltemp: temperature])
}

def setThermostatMode(String mode) {
    def modeMap = ["off":0, "heat":1, "cool":2, "auto":3]
    if (modeMap.containsKey(mode)) {
        sendControlCommand([mode: modeMap[mode]])
    }
}

def setThermostatFanMode(String fanMode) {
    def fanMap = ["auto":0, "on":1]
    if (fanMap.containsKey(fanMode)) {
        sendControlCommand([fan: fanMap[fanMode]])
    }
}

def off() { setThermostatMode("off") }
def heat() { setThermostatMode("heat") }
def cool() { setThermostatMode("cool") }
def auto() { setThermostatMode("auto") }
def fanAuto() { setThermostatFanMode("auto") }
def fanOn() { setThermostatFanMode("on") }

That stuff is probably better for @accounting to try out since my test driver was really just to see if the endpoints even existed and what data they returned. Nothing more than that at this point, but this has it actually pulling in the data and even doing some control functions.

Seems sad to me that coding is going to become like blacksmithing… something people do as an “old timey” thing or hobby only. Progress!

I've recently been using AI for driver creation, as I don't have the patience for writing device drivers, it is just a lot of interface stuff to figure out.

However, I write all my automations into Hubitat Apps, and I code those completely manually. When it comes to apps, I know what I want to do and how I want to do it, but for actual device drivers it takes a lot of discovery and patients to get them working.

Of course, many of my apps use virtual drivers for various things, and I code those manually as well to interface with the app.