I was recently asking about a Zwave module for mini splits (LG mostly). I found some called Aidoo by AirzoneControl. I'm told it pair with HE as a generic thermostat (which is OK). But to expose other parameters a custom driver would be needed. So I asked GROK and Co Pilot to write it. They did! And gave explicit step by step instructions how to implement it. My question, I'm old and I can't be the only one to think of this. Before I dive into this and try it has anyone else done this? Did it work? Is this that easy?
There have been a few posts over the past couple months like yours. I have tried it as well. In my case, it took quite a bit of refining of the AI prompts to get a driver that does what I desired.
This may be a helpful tool, but I think there are still limitations to how well it works. For fairly straightforward things, it certainly does seem to have the ability to create usable code. Maybe not the best written code, but functional nonetheless. In many circumstances, I have found it to write code that has tons of errors, and doesn't work at all. It uses methods and code that don't exist in Hubitat Groovy even when you specify that you are using Hubitat, and point to the Hubitat documentation.
I think there is a bit of an ethical issue as well because a lot of this code is sourced from existing drivers and apps, and at the very least it doesn't cite its sources. At worst, it is straight up ripping off code that it is fed.
I used ChatGPT to jumpstart a project in Swift to connect to the HE events web socket. It didn't give me a finished product but it got me through some early hurdles learning swift.
Now I sometimes use it to create a data mode for some devices that have a complex output. For example, I have a mmWave sensor that returns a good chuck of data. I just copy the output JSON and paste it into ChatGPT and ask it to write a Swift data model. Then I can query the Maker API for that device and have it parsed into an object.
I find it good for solving problems when I am at a loss.
Hi Tony, I did used AI to write several Hubitat codes - including one for Hubitat integration with Airzone Local API. Here's mine, you can just copy paste from below
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"
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()
}
// 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))
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
}
}
I thought the Aidoo would zwave pair as a zwave thermostat? Why the need for a custom driver? I would love to try your driver. Have you tested it? Does it work well? And it looks like it will just be like the generic zwave thermostat or does it do more?
It should
It’s a LAN driver for those who don’t have the Aidoo zwave adapter.
If I opted for the wifi version of Airzone, does it need to be the Pro version to offer local API? Or does the non-pro version also have local API that would work with @potopalex 's driver?
Edit: Upon research, looks like the Pro version is required if you want the local API. From here Local API - Airzone Cloud Developers