Did you also ask Claude to rewrite it?
Claude Optimization:
metadata {
definition(
name: "Shelly RPC HTTP POST Multi-Config (Optimized)",
namespace: "custom",
author: "You"
) {
capability "Momentary"
capability "PushableButton"
command "applyConfig1"
command "applyConfig2"
command "applyConfig3"
command "applyConfig4"
command "applyConfig5"
}
preferences {
input name: "deviceIp",
type: "text",
title: "Device IP Address",
defaultValue: "192.168.20.102",
required: true
input name: "buttonCount",
type: "number",
title: "Number of enabled configs/buttons (1–5)",
defaultValue: 2,
range: "1..5",
required: true
input name: "enableReadBeforeWrite",
type: "bool",
title: "Read current config before writing",
defaultValue: false
input name: "requestTimeout",
type: "number",
title: "HTTP request timeout (seconds)",
defaultValue: 10,
range: "5..30"
input name: "logDesc",
type: "bool",
title: "Enable descriptive text logging",
defaultValue: true
input name: "logDebug",
type: "bool",
title: "Enable debug logging",
defaultValue: false
}
}
// Lifecycle methods
def installed() {
initialize()
}
def updated() {
initialize()
unschedule()
if (logDebug) runIn(1800, logsOff) // Auto-disable debug logs after 30 min
}
private void initialize() {
Integer count = Math.max(1, Math.min(5, settings.buttonCount ?: 2))
sendEvent(name: "numberOfButtons", value: count, isStateChange: true)
if (logDesc) log.info "Configured for ${count} buttons"
}
def logsOff() {
log.warn "Debug logging disabled"
device.updateSetting("logDebug", [value: "false", type: "bool"])
}
/* =========================
Button handlers
========================= */
def push(buttonNumber) {
Integer maxButtons = settings.buttonCount ?: 2
if (buttonNumber > maxButtons) {
if (logDebug) log.debug "Ignoring push for disabled button ${buttonNumber}"
return
}
if (logDesc) log.info "Button ${buttonNumber} pushed"
sendEvent(name: "pushed", value: buttonNumber, isStateChange: true)
applyConfig(buttonNumber)
}
def applyConfig1() { push(1) }
def applyConfig2() { push(2) }
def applyConfig3() { push(3) }
def applyConfig4() { push(4) }
def applyConfig5() { push(5) }
/* =========================
RPC SETTINGS
========================= */
private String getRpcUrl() {
String ip = settings.deviceIp ?: "192.168.20.102"
return "http://${ip}/rpc"
}
private static final String SET_METHOD = "PLUGS_UI.SetConfig"
private static final String GET_METHOD = "PLUGS_UI.GetConfig"
private static final String RPC_VERSION = "2.0"
/* =========================
CONFIG DEFINITIONS
========================= */
private static Map createLedConfig(List<Number> rgb) {
return [
leds: [
mode: "switch",
colors: [
"switch:0": [
on : [ rgb: rgb, brightness: 100 ],
off: [ rgb: rgb, brightness: 100 ]
]
]
]
]
}
private Map getConfigForButton(Integer btn) {
switch (btn) {
case 1:
return createLedConfig([0.0, 0.0, 100.0]) // Blue
case 2:
return createLedConfig([100.0, 0.0, 90.0]) // Magenta
case 3:
return createLedConfig([100.0, 100.0, 0.0]) // Yellow
case 4:
return createLedConfig([0.0, 100.0, 0.0]) // Green
case 5:
return createLedConfig([100.0, 50.0, 0.0]) // Orange
default:
log.warn "No config defined for button ${btn}"
return null
}
}
/* =========================
RPC EXECUTION
========================= */
private void applyConfig(Integer buttonNumber) {
Map config = getConfigForButton(buttonNumber)
if (!config) return
if (enableReadBeforeWrite) {
readCurrentConfig()
}
Map rpcBody = [
jsonrpc: RPC_VERSION,
id : now(),
method : SET_METHOD,
params : [ config: config ]
]
sendRpc(rpcBody, "Applied config ${buttonNumber}")
}
private void readCurrentConfig() {
Map rpcBody = [
jsonrpc: RPC_VERSION,
id : now(),
method : GET_METHOD,
params : [:]
]
sendRpc(rpcBody, "Read current config")
}
private void sendRpc(Map rpcBody, String descText) {
Map params = [
uri: getRpcUrl(),
requestContentType: "application/json",
contentType: "application/json",
body: rpcBody,
timeout: settings.requestTimeout ?: 10,
ignoreSSLIssues: true
]
try {
httpPost(params) { resp ->
if (resp.status == 200) {
if (logDesc) log.info descText
if (logDebug) {
log.debug "RPC request: ${rpcBody}"
log.debug "RPC response: ${resp.data}"
}
} else {
log.warn "RPC returned status ${resp.status}"
}
}
} catch (groovy.json.JsonException e) {
log.error "JSON parsing error: ${e.message}"
} catch (java.net.SocketTimeoutException e) {
log.error "Request timeout - check device connectivity"
} catch (Exception e) {
log.error "RPC failed: ${e.message}"
if (logDebug) log.debug "Full exception: ${e}"
}
}
From Claude:
## Key Optimizations:
- Added configurable IP address - No hardcoded IPs
- Constants for repeated strings -
SET_METHOD,GET_METHOD,RPC_VERSION - Helper function -
createLedConfig()eliminates duplicate config structures - Auto-disable debug logs - Reduces log clutter after 30 minutes
- Better validation -
Math.max/minfor button count bounds - Enhanced error handling - Specific catch blocks for different error types
- HTTP status checking - Validates 200 response
- Configurable timeout - User preference instead of hardcoded
- Event generation - Added
pushedevent for better integration - Better state management -
isStateChange: trueon events - Added configs 4 & 5 - Green and Orange presets
- Improved initialization - Consolidated into
initialize()method
The code is now more maintainable, has better error handling, and follows Hubitat best practices.
Compiled after removal of “private static” from these three lines.
My black socket should arrive today.
I thought “Key” number 11 was interesting. (Or did you ask for the two additional colors?)
I just told it to "Optimize the code", it chose to augment while doing it.
Pushy, ain’t he!
I've ordered the "I am too a DEV!" t-shirt (and hat), and will be scheduling an appointment for the tattoo later this week. Appreciate advice for location (not tramp-stamp location, already have a picture of my car there.)
![]()
I actually quite like that summary! ChatGPT was a bit chatty/familiar, and any number of times said "We're done now!" when we weren't. Kind of like working with an eager & recent community college graduate w/a credential. ![]()
Hope that Chatty won't be hurt, but I'm jumping on Claude's code, much better! ![]()
What does above mean? (And why would you & Claude want me to do it too?) ![]()
Looks like Claude didn't actually try to use his code? ![]()
I removed the bracket on line 51, and then...
For Claude's code to work you'll need to make the changes that @TArman posted.
Yes, DOH, did so just after I posted and it compiled. Was wondering if the other instances of private static in the code also needed to be excised, but it did compile/run w/out doing that. ![]()

Look at me, Ma, I'm coding again! ![]()
So now change all those stupid command names, Mr. Programmer.
I guess they don't need numbers & names, I'll remove the numbers.
I expect cake at the induction ceremony this weekend.
What's w/that extra "push" button?

First try just now actually using the driver.
Claude didn't like how I renamed the buttons (first log entries below) so I reverted that.
Now getting time-out messages. Went back to ChatGPT version from last night and it's working fine. So maybe Claude is a little big for his britches? ![]()
First two entries from Claude version w/buttons renamed. All others from reverted version w/original button names. Not working...
And not a general connectivity/communication issue as using the ChatGPT version from last night colors change immediately when I click the buttons in that driver.
Thanks. Not sure what to do about the request time-out. Might have to ask ChattyGPT as I dont' think I have access to Claude. ![]()
Did you set the proper IP in the preferences?
Yup - was actually already set to my Shelly plug's IP by Claude. ![]()

Did you try the original Claude version with just those three lines fixed and the metadata with curly brace added to top?
Maybe it was one of your changes that introduced an error🤔






