This widget allows controlling and showing the status for Valetudo vacuum robots configured via MQTT.
You should configure your Valetudo thing/items as documented here: openHAB | Valetudo
In particular and most importantly the item names should not be changed from the automatic default names, otherwise the automatic detection of vacuum functionality will not work.
Screenshots
Resources
Widget:
uid: vacuum_widget
tags:
- card
- standalone
props:
parameters:
- context: item
description: Vacuum group item - Must be created from the Homie autodiscovery with default names for all sub-items
label: Vacuum item
name: item
required: true
type: TEXT
groupName: general
- context: url
description: Optional map image URL
label: Map URL
name: map_url
required: false
type: TEXT
groupName: general
- context: url
description: Link to your Valetudo instance
label: Valetudo URL
name: valetudo_url
required: false
type: TEXT
groupName: general
- context: page
description: Optional floor plan page to show a button that links to it
label: Floor plan page
name: floorplan
required: false
type: TEXT
groupName: general
- description: A comma-separated list of "SEGMENT_ID=Name" values, such as "10=Kitchen, 11=Bedroom", to be used instead of the ones provided by Valetudo.
label: User-defined segments
name: segments
required: false
type: TEXT
groupName: general
- description: If set it will be displayed next to the status
label: Title
name: title
required: false
groupName: display
- description: If enabled, fan speed and water grade selection will be available in a menu. If not enabled, buttons will be shown in the widget.
label: Compact fan speed / water grade
name: compact
required: true
type: BOOLEAN
groupName: display
- description: Hide attachments such as dust bin, water tank.. from the widget
label: Hide attachments
name: hide_attachments
required: false
type: BOOLEAN
groupName: display
- description: Hide locate button from widget
label: Hide locate
name: hide_locate
required: false
type: BOOLEAN
groupName: display
parameterGroups:
- name: general
label: General
- name: display
label: Display options
timestamp: Apr 22, 2021, 2:29:15 PM
component: f7-card
slots:
default:
- component: f7-row
config:
style:
justify-content: '=props.compact ? "space-around" : "space-between"'
slots:
default:
- component: f7-col
config:
width: 60
style:
padding: 10px
slots:
default:
- component: oh-button
config:
text: '=(props.title ? (props.title) + ": " : "") + (items[props.item + "_Status"].state + ((items[props.item + "_Statusdetail"].state !== "none") ? (" " + items[props.item + "_Statusdetail"].state) : "")).toUpperCase()'
iconF7: chevron_compact_right
disabled: true
style:
opacity: 1 !important
--f7-button-text-color: var(--f7-text-color)
text-align: left
text-transform: initial
- component: f7-col
config:
width: 30
style:
width: 135px !important
min-width: 135px
padding: 10px
padding-right: 0
slots:
default:
- component: oh-button
config:
style:
float: left
opacity: 1 !important
--f7-button-text-color: var(--f7-text-color)
margin-right: 0
padding-right: 0
border-right: 9
disabled: true
text: ""
iconF7: '=(items[props.item + "_Batterystatus"].state === "charging") ? "bolt" : (items[props.item + "_Batterystatus"].state === "charged") ? "bolt_fill" : "asd"'
- component: oh-button
config:
style:
--f7-button-text-color: var(--f7-text-color)
padding-left: 0
margin-left: 0
border-left: 0
opacity: 1 !important
text: =(items[props.item + "_Batterylevel"].displayState)
disabled: true
iconF7: '=(parseInt(items[props.item + "_Batterylevel"].state) < 30) ? "battery_0" : (parseInt(items[props.item + "_Batterylevel"].state) < 70) ? "battery_25" : "battery_100"'
- component: f7-row
config:
style:
--f7-button-outline-border-color: var(--f7-text-color)
--f7-button-text-color: var(--f7-text-color)
display: '=(props.hide_attachments) ? "none" : "flex"'
justify-content: center
padding-bottom: 15px
slots:
default:
- component: oh-button
config:
disabled: true
text: Dust bin
iconF7: tray
outline: '=(items[props.item + "_Dustbin"]).state === "ON" ? true : false'
style:
opacity: .7 !important
- component: oh-button
config:
disabled: true
text: Water tank
iconF7: drop
outline: '=(items[props.item + "_Watertank"]).state === "ON" ? true : false'
style:
opacity: .7 !important
- component: oh-button
config:
disabled: true
text: Mop
iconF7: drop_triangle
outline: '=items[props.item + "_Mop"].state === "ON" ? true : false'
style:
opacity: .7 !important
- component: f7-row
config:
style:
justify-content: space-around
padding-top: 5px
border-top: '=themeOptions.dark === "dark" ? "solid 1px rgba(255, 255, 255, 0.2)" : "solid 1px rgba(0, 0, 0, 0.2)"'
slots:
default:
- component: oh-button
config:
text: '=(items[props.item + "_Status"].state === "paused") ? "Resume" : (items[props.item + "_Status"].state === "cleaning") ? "Pause" : "Start"'
iconF7: '=(items[props.item + "_Status"].state === "cleaning") ? "pause_fill" : "play_fill"'
large: true
active: =(items[props.item + "_Status"].state === "cleaning")
action: command
actionItem: =(props.item + "_Operation")
actionCommand: '=(items[props.item + "_Status"].state === "cleaning") ? "PAUSE" : "START"'
style:
padding-left: 20px
padding-right: 20px
- component: oh-button
config:
text: Stop
iconF7: '=(items[props.item + "_Status"].state === "docked" || items[props.item + "_Status"].state === "idle") ? "stop" : "stop_fill"'
large: true
action: command
actionItem: =(props.item + "_Operation")
actionCommand: STOP
style:
padding-left: 20px
padding-right: 20px
- component: oh-button
config:
text: '=(items[props.item + "_Status"].state === "docked") ? "Docked" : (items[props.item + "_Status"].state === "returning") ? "Returning" : "Dock"'
iconF7: '=(items[props.item + "_Status"].state === "docked") ? "house_alt" : "house_alt_fill"'
large: true
active: =(items[props.item + "_Status"].state === "returning")
action: command
actionItem: =(props.item + "_Operation")
actionCommand: HOME
style:
padding-left: 20px
padding-right: 20px
- component: oh-button
config:
text: Locate
iconF7: placemark
large: true
action: command
actionItem: =(props.item + "_Locate")
actionCommand: PERFORM
style:
display: '=(props.hide_locate ? "none" : undefined)'
padding-left: 20px
padding-right: 20px
- component: oh-button
config:
text: Clean room
iconF7: viewfinder
large: true
action: options
actionItem: =props.item + "_Cleansegments"
actionOptions: '=props.segments ? "{\"segment_ids\":[\"" +props.segments.replaceAll("=","\"]}=" ).replaceAll(", ", ", {\"segment_ids\":[\"").slice(0, -1) : "{\"segment_ids\":[" + JSON.stringify(JSON.parse(items[props.item + "_Mapsegments"].state)).replaceAll("\{","").replaceAll("\}","").replaceAll("\":\"","\"]}=" ).replaceAll("\",\"",", {\"segment_ids\":[\"").slice(0, -1)'
style:
display: '=((items[props.item + "_Mapsegments"].state !== "" && items[props.item + "_Mapsegments"].state !== "{}") || props.segments) && items[props.item + "_Cleansegments"] !== undefined ? undefined : "none"'
- component: f7-row
config:
style:
margin-top: 15px
display: '=((items[props.item + "_Fanspeed"] !== undefined && !props.compact) ? undefined : "none")'
slots:
default:
- component: f7-col
config:
width: 10
slots:
default:
- component: f7-icon
config:
f7: wind
style:
padding-left: 15px
- component: f7-col
config:
width: 90
style:
margin-bottom: 5px
slots:
default:
- component: f7-segmented
slots:
default:
- component: oh-repeater
config:
for: item
sourceType: itemCommandOptions
itemOptions: =props.item + "_Fanspeed"
containerClass: row
fragment: true
slots:
default:
- component: oh-button
config:
text: =loop.item.label
small: true
action: command
actionItem: =props.item + "_Fanspeed"
actionCommand: =loop.item.command
active: =items[props.item + "_Fanspeed"].state === loop.item.command
- component: f7-row
config:
style:
display: '=((items[props.item + "_Watergrade"] !== undefined && !props.compact) ? undefined : "none")'
slots:
default:
- component: f7-col
config:
width: 10
slots:
default:
- component: f7-icon
config:
f7: drop
style:
padding-left: 15px
- component: f7-col
config:
width: 90
style:
margin-bottom: 15px
slots:
default:
- component: f7-segmented
slots:
default:
- component: oh-repeater
config:
for: item
sourceType: itemCommandOptions
itemOptions: =props.item + "_Watergrade"
containerClass: row
fragment: true
slots:
default:
- component: oh-button
config:
text: =loop.item.label
small: true
action: command
actionItem: =props.item + "_Watergrade"
actionCommand: =loop.item.command
active: =items[props.item + "_Watergrade"].state === loop.item.command
- component: f7-row
config:
style:
display: '=(props.compact) ? undefined : "none"'
justify-content: space-around
slots:
default:
- component: f7-col
config:
style:
--f7-button-text-color: var(--f7-text-color)
display: '=(items[props.item + "_Fanspeed"] !== undefined ? undefined : "none")'
width: '=(items[props.item + "_Watergrade"] !== undefined ? 50 : 100)'
slots:
default:
- component: oh-button
config:
text: '="Fan: " + items[props.item + "_Fanspeed"].state'
iconF7: wind
large: true
action: options
actionItem: =props.item + "_Fanspeed"
- component: f7-col
config:
style:
--f7-button-text-color: var(--f7-text-color)
display: '=(items[props.item + "_Watergrade"] !== undefined ? undefined : "none")'
width: '=(items[props.item + "_Fanspeed"] !== undefined ? 50 : 100)'
slots:
default:
- component: oh-button
config:
text: '="Water: " + items[props.item + "_Watergrade"].state'
iconF7: drop
large: true
action: options
actionItem: =props.item + "_Watergrade"
- component: f7-row
config:
style:
display: '=((props.valetudo_url || props.floorplan || props.segments) ? undefined : "none")'
justify-content: space-around
slots:
default:
- component: f7-col
config:
style:
--f7-button-text-color: var(--f7-text-color)
display: '=(props.floorplan ? undefined : "none")'
width: "=(props.valetudo_url) ? 50 : 100"
slots:
default:
- component: oh-button
config:
text: Floor plan
iconF7: map
large: true
action: navigate
actionPage: =props.floorplan
- component: f7-col
config:
style:
--f7-button-text-color: var(--f7-text-color)
display: '=(props.valetudo_url ? undefined : "none")'
width: "=(props.floorplan ? 50 : 100)"
slots:
default:
- component: oh-button
config:
text: Valetudo
iconF7: link
large: true
action: url
actionUrl: =props.valetudo_url
actionUrlSameWindow: false
- component: f7-row
config:
style:
display: '=(props.map_url ? undefined : "none")'
slots:
default:
- component: oh-image
config:
url: =props.map_url
lazy: false
style:
width: 100%
height: auto
- component: f7-row
config: {}
slots:
default:
- component: oh-image
config:
url: =atob(items[props.item + "_Map"].state)
lazy: false
style:
width: 100%
height: auto