I just asked claude to create a widget in the same style as my dishwasher widget, gave it a photo of my machine and after four round trips I ended up having this:
This is not an exact replica of my machine but I think it is close enough. This would have taken me probably a few evenings to put this together myself ![]()
If you are interested, this is the code:
uid: espresso_machine
tags: []
props:
parameters:
- description: Title of the card
label: Title
name: title
required: false
type: TEXT
- description: The card footer
label: Footer
name: footer
required: false
type: TEXT
- description: Header text above machine
label: Header
name: header
required: false
type: TEXT
- context: item
label: Switch Item
name: switchItem
required: true
type: TEXT
parameterGroups: []
timestamp: Jun 25, 2026, 12:48:31 PM
component: f7-card
config:
title: =(props.title)
slots:
default:
- component: f7-card-content
config:
style:
height: 175px
slots:
default:
- component: f7-col
config:
class:
- display-flex
- flex-direction-column
- align-items-center
- justify-content-space-evenly
style:
height: 100%
slots:
default:
- component: f7-block-header
slots:
default:
- component: Label
config:
text: =props.header
- component: svg
config:
viewBox: 0 0 160 145
style:
height: 100px
width: auto
slots:
default:
- component: defs
slots:
default:
- component: linearGradient
config:
id: st
x1: "0"
y1: "0"
x2: "1"
y2: "0"
slots:
default:
- component: stop
config:
offset: 0%
stop-color: "#989898"
- component: stop
config:
offset: 28%
stop-color: "#e2e2e2"
- component: stop
config:
offset: 60%
stop-color: "#cacaca"
- component: stop
config:
offset: 100%
stop-color: "#929292"
- component: linearGradient
config:
id: stv
x1: "0"
y1: "0"
x2: "0"
y2: "1"
slots:
default:
- component: stop
config:
offset: 0%
stop-color: "#d6d6d6"
- component: stop
config:
offset: 50%
stop-color: "#bcbcbc"
- component: stop
config:
offset: 100%
stop-color: "#868686"
- component: linearGradient
config:
id: kn
x1: "0"
y1: "0"
x2: "1"
y2: "1"
slots:
default:
- component: stop
config:
offset: 0%
stop-color: "#565656"
- component: stop
config:
offset: 100%
stop-color: "#1a1a1a"
- component: linearGradient
config:
id: gh
x1: "0"
y1: "0"
x2: "1"
y2: "1"
slots:
default:
- component: stop
config:
offset: 0%
stop-color: "#ececec"
- component: stop
config:
offset: 45%
stop-color: "#b8b8b8"
- component: stop
config:
offset: 100%
stop-color: "#848484"
- component: linearGradient
config:
id: pf
x1: "0"
y1: "0"
x2: "0"
y2: "1"
slots:
default:
- component: stop
config:
offset: 0%
stop-color: "#d0d0d0"
- component: stop
config:
offset: 100%
stop-color: "#868686"
- component: linearGradient
config:
id: cup
x1: "0"
y1: "0"
x2: "0"
y2: "1"
slots:
default:
- component: stop
config:
offset: 0%
stop-color: "#f8f8f8"
- component: stop
config:
offset: 100%
stop-color: "#e0e0e0"
- component: rect
config:
x: "36"
y: "4"
width: "20"
height: "14"
rx: "3"
fill: url(#cup)
stroke: "#cccccc"
stroke-width: "0.5"
- component: path
config:
d: M56 9 Q61 9 61 13 Q61 17 56 17
fill: none
stroke: "#cccccc"
stroke-width: "1.2"
stroke-linecap: round
- component: line
config:
x1: "36"
y1: "18"
x2: "56"
y2: "18"
stroke: "#bbbbbb"
stroke-width: "0.5"
- component: rect
config:
x: "70"
y: "4"
width: "20"
height: "14"
rx: "3"
fill: url(#cup)
stroke: "#cccccc"
stroke-width: "0.5"
- component: path
config:
d: M90 9 Q95 9 95 13 Q95 17 90 17
fill: none
stroke: "#cccccc"
stroke-width: "1.2"
stroke-linecap: round
- component: line
config:
x1: "70"
y1: "18"
x2: "90"
y2: "18"
stroke: "#bbbbbb"
stroke-width: "0.5"
- component: rect
config:
x: "104"
y: "4"
width: "20"
height: "14"
rx: "3"
fill: url(#cup)
stroke: "#cccccc"
stroke-width: "0.5"
- component: path
config:
d: M124 9 Q129 9 129 13 Q129 17 124 17
fill: none
stroke: "#cccccc"
stroke-width: "1.2"
stroke-linecap: round
- component: line
config:
x1: "104"
y1: "18"
x2: "124"
y2: "18"
stroke: "#bbbbbb"
stroke-width: "0.5"
- component: rect
config:
x: "28"
y: "18"
width: "104"
height: "108"
rx: "5"
fill: url(#stv)
stroke: "#aaaaaa"
stroke-width: "0.8"
- component: rect
config:
x: "28"
y: "18"
width: "104"
height: "6"
rx: "3"
fill: url(#st)
stroke: "#bbbbbb"
stroke-width: "0.5"
- component: rect
config:
x: "32"
y: "26"
width: "96"
height: "88"
rx: "2"
fill: "#cbcbcb"
stroke: "#b8b8b8"
stroke-width: "0.4"
opacity: "0.8"
- component: line
config:
x1: "40"
y1: "30"
x2: "40"
y2: "110"
stroke: "#e4e4e4"
stroke-width: "1.5"
opacity: "0.5"
- component: line
config:
x1: "50"
y1: "30"
x2: "50"
y2: "110"
stroke: "#e4e4e4"
stroke-width: "0.5"
opacity: "0.25"
- component: circle
config:
cx: "50"
cy: "44"
r: "13"
fill: url(#kn)
stroke: "#666666"
stroke-width: "1"
- component: circle
config:
cx: "50"
cy: "44"
r: "9"
fill: none
stroke: "#505050"
stroke-width: "0.5"
stroke-dasharray: 2 2
- component: path
config:
d: M47.5 41 Q48.5 38.5 50 41 Q51.5 43.5 52.5 41
fill: none
stroke: "#aaaaaa"
stroke-width: "0.9"
stroke-linecap: round
- component: line
config:
x1: "50"
y1: "32"
x2: "50"
y2: "36"
stroke: "#888888"
stroke-width: "1"
stroke-linecap: round
- component: circle
config:
cx: "50"
cy: "57"
r: "3.5"
fill: "#b0b0b0"
stroke: "#999999"
stroke-width: "0.5"
- component: line
config:
x1: "50"
y1: "57"
x2: "34"
y2: "85"
stroke: "#c8c8c8"
stroke-width: "3.5"
stroke-linecap: round
- component: circle
config:
cx: "33"
cy: "87"
r: "3"
fill: "#a8a8a8"
stroke: "#909090"
stroke-width: "0.5"
- component: line
config:
x1: "31"
y1: "89"
x2: "35"
y2: "89"
stroke: "#888888"
stroke-width: "1.2"
stroke-linecap: round
- component: circle
config:
cx: "110"
cy: "44"
r: "13"
fill: url(#kn)
stroke: "#666666"
stroke-width: "1"
- component: circle
config:
cx: "110"
cy: "44"
r: "9"
fill: none
stroke: "#505050"
stroke-width: "0.5"
stroke-dasharray: 2 2
- component: path
config:
d: M110 38.5 Q113 42 110 46 Q107 42 110 38.5Z
fill: "#888888"
stroke: none
- component: line
config:
x1: "110"
y1: "32"
x2: "110"
y2: "36"
stroke: "#888888"
stroke-width: "1"
stroke-linecap: round
- component: circle
config:
cx: "110"
cy: "57"
r: "3.5"
fill: "#b0b0b0"
stroke: "#999999"
stroke-width: "0.5"
- component: line
config:
x1: "110"
y1: "57"
x2: "126"
y2: "85"
stroke: "#c8c8c8"
stroke-width: "3.5"
stroke-linecap: round
- component: circle
config:
cx: "127"
cy: "87"
r: "3"
fill: "#a8a8a8"
stroke: "#909090"
stroke-width: "0.5"
- component: line
config:
x1: "125"
y1: "89"
x2: "129"
y2: "89"
stroke: "#888888"
stroke-width: "1.2"
stroke-linecap: round
- component: circle
config:
cx: "80"
cy: "40"
r: "4"
fill: '=items[props.switchItem].state == "ON" ? "#cc2222" : "#444444"'
stroke: '=items[props.switchItem].state == "ON" ? "#991111" : "#333333"'
stroke-width: "0.4"
- component: circle
config:
cx: "80"
cy: "40"
r: "6"
fill: none
stroke: "#ff3333"
stroke-width: "0.8"
opacity: '=items[props.switchItem].state == "ON" ? "0.5" : "0"'
- component: circle
config:
cx: "80"
cy: "60"
r: "17"
fill: url(#gh)
stroke: "#aaaaaa"
stroke-width: "1.3"
- component: circle
config:
cx: "80"
cy: "60"
r: "12"
fill: "#bfbfbf"
stroke: "#b0b0b0"
stroke-width: "0.5"
- component: circle
config:
cx: "80"
cy: "60"
r: "7"
fill: "#d2d2d2"
stroke: "#a8a8a8"
stroke-width: "0.5"
- component: circle
config:
cx: "77"
cy: "57"
r: "3"
fill: "#eeeeee"
opacity: "0.75"
- component: path
config:
d: M65 74 Q63 83 62 88 L60 96 Q80 103 100 96 L98 88 Q97 83 95 74Z
fill: url(#pf)
stroke: "#a8a8a8"
stroke-width: "0.7"
- component: rect
config:
x: "61"
y: "96"
width: "38"
height: "11"
rx: "5.5"
fill: "#1a1a1a"
stroke: "#2e2e2e"
stroke-width: "0.5"
- component: rect
config:
x: "63"
y: "97.5"
width: "34"
height: "3"
rx: "1.5"
fill: "#2e2e2e"
opacity: "0.5"
- component: rect
config:
x: "28"
y: "114"
width: "104"
height: "5"
rx: "2"
fill: "#a8a8a8"
stroke: "#989898"
stroke-width: "0.4"
- component: rect
config:
x: "30"
y: "119"
width: "100"
height: "7"
rx: "1.5"
fill: "#868686"
stroke: "#767676"
stroke-width: "0.4"
- component: path
config:
d: M33 122 Q37 120 41 122 Q45 124 49 122 Q53 120 57 122 Q61 124 65 122 Q69 120
73 122 Q77 124 81 122 Q85 120 89 122 Q93 124 97 122
Q101 120 105 122 Q109 124 113 122 Q117 120 121 122
Q125 124 127 122
fill: none
stroke: "#989898"
stroke-width: "0.7"
- component: circle
config:
cx: "46"
cy: "132"
r: "5"
fill: "#646464"
stroke: "#525252"
stroke-width: "0.4"
- component: circle
config:
cx: "114"
cy: "132"
r: "5"
fill: "#646464"
stroke: "#525252"
stroke-width: "0.4"
- component: oh-toggle
config:
item: =props.switchItem
colorTheme: red
- component: f7-card-footer
slots:
default:
- component: Label
config:
text: =props.footer
