GUI Recorder – manage recorder & clean up your DB, no YAML

The problem

If you’ve ever tried to stop a chatty sensor from bloating your recorder database, you know the drill: open configuration.yaml, hand-write an exclude: block, guess at the right entity_globs, restart, and hope you didn’t typo an entity_id. And when the DB is 2 GB and you have no idea which entities are responsible — well, good luck, because that information isn’t exposed anywhere in the UI.

I got tired of doing this by hand, so I built GUI Recorder.

What it does

It adds a sidebar panel to manage your recorder configuration and database maintenance — without touching configuration.yaml.

  • Per-device and per-entity toggles — turn recording on/off with a switch. No YAML.
  • Database statistics — see total / current / obsolete record counts, which entities are eating the most rows, disk size, and the DB path. Finally know what is bloating your database.
  • Maintenance actions — purge the DB by retention, purge the full history of excluded entities, repack (VACUUM) to reclaim disk, all from buttons.
  • Orphan / obsolete cleanup — spot exclusions that no longer match any entity, and records left behind by deleted entities.
  • Guided migration — if you already have a recorder: block, it imports your existing config and walks you through switching over in 3 steps.
  • Manual exclusions field — for the domain/glob/event_type filters that don’t fit the per-entity model.

Install

It’s in the default HACS catalog — just search for “GUI Recorder” in HACS → Integrations, install, restart, and add the integration. No custom repository needed.

GitHub (source, screenshots, full docs): GitHub - ideaalab/gui-recorder: Home Assistant custom component: manage recorder config and DB maintenance from a sidebar GUI panel (SQLite only) · GitHub

Important caveats

  • SQLite only. MariaDB/PostgreSQL are explicitly rejected during setup — the DB analysis reads the SQLite file directly, so other backends aren’t supported.
  • Purges are permanent. Always take a full HA backup before using the maintenance or migration features. Deleted recorder history can’t be recovered.

It’s still pre-1.0 and actively maintained — I’ve been shipping fixes as people report things. Feedback, bug reports, and feature ideas welcome, either here or on the GitHub issue tracker. Hope it’s useful to someone else who’s been fighting their recorder config by hand.

8 Likes

Wow, this is long overdue, thanks!

With all the other stuff in HA being GUI-fied, the recorder remains obscure, and a real trap for non-technical users.

One question: Does this require me to give up my hard-earned YAML exclusions? Sure, I have backups, but I am really starting to distrust configuration settings which are hidden behind a UI, with no other way to review, manipulate, copy-and-paste them or back them up.

Finally, and I suspect this is outside the scope of an app/add-on like this, would there be any way to set up different retention periods for different entities? This is probably the biggest weakness of the Recorder; a one-size-fits-all Recorder “keep days” setting. Some things don’t need to be kept forever, while others do.

1 Like

Thanks, glad it resonates — the recorder being the last un-GUI’d trap was exactly what pushed me to build this.

On giving up your YAML exclusions: you don’t, and I share your distrust of settings buried in an opaque UI. A couple of points:

  • The migration flow imports your existing recorder: block rather than discarding it — your hand-crafted exclusions come across as the starting point, not a blank slate.
  • The effective config isn’t hidden. It’s written to a plain, human-readable gui_recorder.yaml that’s included from your configuration.yaml via recorder: !include gui_recorder.yaml. You can open it, read it, copy-paste it, diff it, back it up — it’s just YAML. The panel is a convenience layer on top of that file, not a replacement for being able to see it.
  • There’s also a “Manual exclusions” field for the domains / entity_globs / event_types filters that don’t fit the per-entity toggle model, so you’re not forced to give those up either.

The one caveat: gui_recorder.yaml is regenerated by the plugin, so you shouldn’t hand-edit that specific file (use the panel / manual field instead) — but everything it produces is right there in plaintext for you to review and back up.

On per-entity retention: you’re right that it’s the recorder’s biggest weakness, and right that it’s out of scope here — but the limitation is Home Assistant’s, not the plugin’s. HA’s recorder only exposes a single global purge_keep_days; there’s no per-entity retention API to build a GUI on top of. The plugin is a front-end for the recorder’s existing config surface, so it can’t add capabilities the recorder itself doesn’t have.

Two things that get you partway there today:

  • Long-term statistics — for numeric sensors with a state_class, HA already keeps hourly aggregates forever, independent of purge_keep_days. So “keep the trend but drop the noise” is often already handled for the sensors that matter most.
  • You can approximate tiered retention with an automation calling the recorder.purge_entities service on a schedule with different keep_days per group of entities — vanilla HA, no add-on needed.

Building real per-entity retention would mean writing into the recorder’s SQLite DB alongside HA’s own writer, which is a different (and riskier) kind of tool than this one. Deliberately staying out of that lane.

Amazing extension, trying it today , my database is 20G+ and is a pain to manage…

20Gb DB? Im worried because mine its almost 2Gb :smiley:
Is it safe to have that big db? Let me know if it helps to reduce its size and if it works fine.

Seems like a great integration!

However :slight_smile: ---- I just ran into a big gotcha.

My database is not in the normal folder (config) - I moved it to the “share” folder.
Don’t even ask why :slight_smile: - it’s just the way it is.

When I installed your integration, it completely ignored that fact and just started a “new” database in the normal folder.

So unfortunately I had to revert back to my backup.
It is a pitty - really would like to use this.

Do you think you could make a different database location an option in the future?

Thanks for the report — turns out you hit two bugs, not one.

GUI Recorder never touches the DB itself (read-only access only), so that empty database was created by HA’s recorder — meaning db_url got lost somewhere in the config. Two things caused it:

  1. Migration wasn’t copying db_url over when importing your old config.
  2. Worse: nothing stopped you from running Steps 2/3 (disable + enable) without running Step 1 (import) first. If that happens, gui_recorder.yaml gets written with defaults — no exclusions, no db_url, everything reset.

Both fixed in v0.8.37: db_url now gets imported properly, and steps 2/3 are blocked until you’ve run the import.

Since you already restored your backup, your old recorder: block with db_url should still be active — just update and run through the steps again, in order this time. After Step 1 you’ll see your db_url listed in the summary.

Out of curiosity, do you remember if you clicked Import first or went straight to Step 2? Not that it matters now, just want to know which bug you hit. Sorry about the backup restore, that shouldn’t have happened.

2 Likes

I did go through all the steps from the beginning.
I don’t think I ommited step 1.
But as always - I could have had a brain fart, so unfortunately I cannot guarantee it :slight_smile:

Thank you for the response - I will try out the new version as soon as I find a bit of time.

I’m quite interested in that.
How is a separate recorder.yaml file handled when included via recorder: !include recorder.yaml in the configuration?

recorder.yaml is commented and gui_recorder.yaml is included in the config file with the configurator of the addon.

So here a quick update - I haven’t done any extensive testing yet, but the update seems to have done the trick.
My database that sits in a “non-standard” folder is working as advertised.

Great work – this makes the whole database operation a lot more transparent and gives you a good understanding which sensors occupy unnecessary space in the history.

1 Like

And as the database is a subject that must be taken seriously, here is another one:
So what happens with what i have in my recorder.yaml, is it imported (as the doc only mention importing from configuration.yaml) ? As my recorder is set this way with less than a 100 entities included and 1 domain (person), everything else is excluded by default ; would it be taken as it is ?

purge_keep_days: 10
auto_purge: true
auto_repack: true
commit_interval: 30
include:
  entities:
   ...
  domains:
  ...

I finally got around to installing it. Looks great!!

I did add one step to the manual install process: take a backup of configuration.yaml, and save it for future reference. I went one step further and saved an extract of just my old recorder: section, too.

I didn’t really gain any huge insights I didn’t already know by reviewing the database from time to time with some queries I’d set up, but this sure is easier. Probably the biggest benefit right off the bat for me was to identify some entity IDs I’d excluded, but were no longer in the system. Not hurting anything really, but still nice to tidy up.

This is the sort of thing which should ship with the core HA. New users would really benefit from a clear understanding of what’s in their Recorder database. It’s a bit sad to see so many come here asking what this huge database file is taking up space on their system and in their backups.

Nice work!!

1 Like

Update from a day of using GUI Recorder:

  1. I still love it!
  2. I did notice that the database size reading doesn’t seem to match the actual database file. For example, it’s now showing “DB size: 7.17 MB.” The home-assistant_v2.db file is currently 3,268 KB. I hit “Update data” and it changed to 5.83 MB. Still not exactly what I’m seeing for file size.
  3. There’s a lot of data on the screen. It might be nice to be able to collapse sections so users can skip over the things they’re not interested in at the moment.

Anyway, I still think every new HA user should use something like this, and understand how their choices impact the size of the database. As you can see by the numbers above, I’ve been aggressive about keeping a lean database for years, after being burned once, before I understood how Recorder works.

So here is an odd one Martin84:

GUI Recorder took over my “exclusion” from config.yaml
Except I did not work with exclusion, but instead only included - here a snippet:

While this worked quite well, I ran into problems when I just tried to start to get data into the recorder from a sensor that was installed BEFORE I installed GUI Recorder and was previously NOT on my include list,

Despite GUI Recorder saying that it will do it - see picture:


… actually nothing happens.
I am currently unable to record a history of this sensor.

So my question - am I doing it wrong - and if so how to fix it.
Or is this maybe a bug?

Here my gui_recorder.yaml:

This file is managed by the GUI Recorder integration.

Manual edits will be overwritten.

auto_purge: true
auto_repack: true
purge_keep_days: 370
commit_interval: 5
db_url: sqlite:////share/home-assistant_v2.db

exclude:
entities:

  • binary_sensor.internet_available
  • binary_sensor.ioniq_5_lichter_defekt
  • binary_sensor.ioniq_tire_pressure
  • binary_sensor.meteoalarm_nidwalden
  • binary_sensor.samsung_refrigerator
  • cover.rollos_haus
  • cover.rollos_og
  • cover.rollos_ug
  • device_tracker.ibeacon_slk
  • input_boolean.alarm_check_time_and_date_boiler
  • input_boolean.auto_muss_geladen_werden
  • input_boolean.autoschloss_activator
  • input_boolean.gastelicht_nur_einmal_am_tag_einschalten
  • input_boolean.gute_nacht_script_active
  • input_boolean.klima_ioniq_5_activator
  • input_boolean.lichtwecker_status
  • input_boolean.luftung_vereist
  • input_boolean.mute_soundbar
  • input_boolean.roomba_reinigen
  • input_boolean.speedtest_currently_running
  • input_boolean.staubsauger_activator
  • input_boolean.umschalter_boilertemperatur_uber_59_grad
  • input_boolean.velo_fertig_geladen
  • input_boolean.velo_ladt
  • input_boolean.wait_hinterhof
  • input_boolean.wait_test_1
  • input_boolean.wait_test_2
  • input_boolean.wait_vogelhaus_1
  • input_boolean.wait_vogelhaus_2
  • input_boolean.wait_vogelhaus_3
  • input_boolean.wait_vordertuer
  • input_boolean.wasseralarm_active
  • input_number.smoke_flur_ug_button_pressed
  • input_number.smoke_garage_button_pressed
  • input_number.smoke_veloschuppen_button_pressed
  • input_number.temperaturknopf
  • input_number.test_solltemp_schlaf
  • input_text.aktie_1
  • input_text.aktie_2
  • input_text.aktie_3
  • input_text.aktie_4
  • input_text.datum_batteriewechsel_ankleide
  • input_text.datum_batteriewechsel_bad
  • input_text.datum_batteriewechsel_balkon
  • input_text.datum_batteriewechsel_buro
  • input_text.datum_batteriewechsel_bwm_gaeste
  • input_text.datum_batteriewechsel_bwm_schlaf
  • input_text.datum_batteriewechsel_dusche
  • input_text.datum_batteriewechsel_fahrrad
  • input_text.datum_batteriewechsel_fenstersensor_balkon_wohnzimmer
  • input_text.datum_batteriewechsel_gaeste
  • input_text.datum_batteriewechsel_garage
  • input_text.datum_batteriewechsel_hinterhof
  • input_text.datum_batteriewechsel_hobbykeller
  • input_text.datum_batteriewechsel_ikea_wassermelder_waschkuche
  • input_text.datum_batteriewechsel_nuki_garage
  • input_text.datum_batteriewechsel_nuki_garage_keypad
  • input_text.datum_batteriewechsel_nuki_haustur
  • input_text.datum_batteriewechsel_nuki_haustur_keypad
  • input_text.datum_batteriewechsel_og
  • input_text.datum_batteriewechsel_reduit
  • input_text.datum_batteriewechsel_safe
  • input_text.datum_batteriewechsel_smoke_flur_ug
  • input_text.datum_batteriewechsel_smoke_garage
  • input_text.datum_batteriewechsel_smoke_veloschuppen
  • input_text.datum_batteriewechsel_test_1
  • input_text.datum_batteriewechsel_test_2
  • input_text.datum_batteriewechsel_toilette
  • input_text.datum_batteriewechsel_tursensor_garage_tur
  • input_text.datum_batteriewechsel_ug
  • input_text.datum_batteriewechsel_vogelhaus_1
  • input_text.datum_batteriewechsel_vogelhaus_2
  • input_text.datum_batteriewechsel_vogelhaus_3
  • input_text.datum_batteriewechsel_vordertur
  • input_text.datum_batteriewechsel_waschkuche
  • input_text.datum_batteriewechsel_wassermelder_bad
  • input_text.datum_batteriewechsel_wassermelder_dusche
  • input_text.datum_batteriewechsel_wassermelder_kuche
  • input_text.datum_batteriewechsel_wassermelder_waschkuche
  • input_text.datum_refill_erdsonde
  • input_text.datum_refill_heizung
  • notify.file
  • notify.file_2
  • notify.file_3
  • notify.file_4
  • scene.nachttisch_frank_blau
  • scene.nachttisch_frank_gemutlich
  • scene.nachttisch_gaste_blau
  • scene.test
  • scene.untergeschoss_aus
  • scene.untergeschoss_weiss
  • sensor.aktie_1_change_negative
  • sensor.blank_blank
  • sensor.date_neu
  • sensor.day_of_the_week
  • sensor.heizung_ankleide_device_temperature
  • sensor.heizung_ankleide_voltage
  • sensor.heizung_ausschalten
  • sensor.heizung_bad_switch_0_power
  • sensor.heizung_bad_switch_0_voltage
  • sensor.heizung_buro_power
  • sensor.heizung_buro_voltage
  • sensor.heizung_druck_input_2_pulse_counter
  • sensor.heizung_druck_input_2_pulse_counter_frequency
  • sensor.heizung_druck_voltmeter
  • sensor.heizung_dusche_switch_0_power
  • sensor.heizung_dusche_switch_0_voltage
  • sensor.heizung_ein
  • sensor.heizung_essen_kuche_power
  • sensor.heizung_essen_kuche_voltage
  • sensor.heizung_gaste_power
  • sensor.heizung_gaste_voltage
  • sensor.heizung_schlafzimmer_power
  • sensor.heizung_schlafzimmer_voltage
  • sensor.heizung_toilette_power
  • sensor.heizung_toilette_voltage
  • sensor.heizung_wohnzimmer_balkon_power
  • sensor.heizung_wohnzimmer_balkon_voltage
  • sensor.heizung_wohnzimmer_strasse_power
  • sensor.heizung_wohnzimmer_strasse_voltage
  • sensor.kfn_modem_down
  • sensor.kfn_modem_up
  • sensor.nuki_garage_rssi
  • sensor.nuki_haustur_rssi
  • sensor.rollo_strasse_device_temperature
  • sensor.samsung_refrigerator_connection
  • sensor.samsung_refrigerator_mesh_node
  • sensor.stromverbrauch_haus_phase_a_apparent_power
  • sensor.stromverbrauch_haus_phase_a_power_factor
  • sensor.stromverbrauch_haus_phase_b_apparent_power
  • sensor.stromverbrauch_haus_phase_b_power_factor
  • sensor.stromverbrauch_haus_phase_c_apparent_power
  • sensor.stromverbrauch_haus_phase_c_power_factor
  • sensor.stromverbrauch_haus_total_apparent_power
  • sensor.stromverbrauch_haus_total_current
  • sensor.tag_n_des_jahres
  • sensor.tag_n_des_jahres_stundengenau
  • sensor.tage_seit_letztem_fritzbox_4060_start
  • sensor.tage_seit_letztem_ha_reboot
  • sensor.time_neu
  • sensor.veloschuppen_steckdosen_switch_0_voltage
  • sensor.volume_soundbar
  • sensor.watchman_anzahl_fehler

include:
entity_globs:

  • binary_sensor.internet_available
  • binary_sensor.meteo*
  • binary_sensor.nuki*
  • binary_sensor.roomb*
  • binary_sensor.vordertur*
  • binary_sensor.wassermelder_*
  • binary_sensor:fenster
  • binary_sensor:smoke
  • binary_sensor:tursensor
  • cover.wasserhahn_waschmaschine
  • device_tracker.*frank"
  • device_tracker.nuri
  • device_tracker.schatzeli
  • device_tracker.slk
  • device_tracker.ioniq_5_location
  • group.*
  • input_number.anzahl*
  • input_number.investment
  • light.*
  • samsung_refrigerator_local_power
  • sensor.batterie
  • sensor.battery
  • sensor.eaton
  • sensor.luftfeuchtigkeit
  • sensor.temperatur
  • sensor.aktie*
  • sensor.chf*
  • sensor.database_in*
  • sensor.depot*
  • sensor.disk_use_p*
  • sensor.druck*
  • sensor.finger*
  • sensor.heizung*
  • sensor.home_assistant_v2*
  • sensor.ibeacon*
  • sensor.ioniq_*
  • sensor.kuhlschrank_verbrauch_pro_tag
  • sensor.last_boot
  • sensor.last_homeassistant_partial_backup
  • sensor.load_1m
  • sensor.lueftung_power
  • sensor.luftung*
  • sensor.mah*
  • sensor.memory_use_percent
  • sensor.mercedes*
  • sensor.nuki*
  • sensor.processor*
  • sensor.roomb*
  • sensor.samsung_refrigerator_local_energy_used
  • sensor.shelly_addon_status
  • sensor.solar*
  • sensor.stand_stromzahler
  • sensor.strom_durch*
  • sensor.stromverbrauch*
  • sensor.stromverbrauch_haus_phase_a_active_power
  • sensor.stromverbrauch_haus_total_active_energy
  • sensor.stromverbrauch_haus_total_active_power
  • sensor.swap*
  • sensor.veloschuppen*
  • sensor.verbrauch_velosteckdose_in_wh
  • sensor.verbrauchsmessung*
  • sensor.wasser*
  • sensor.wassermelder_*_battery
  • sensor.wasserpumpe*
  • sensor.water*
  • switch.garage*
  • switch.heizung*
  • switch.licht*
  • switch.lueftung*
  • switch.pc_reset"
  • switch.pc_start*
  • switch.verbrauch*
  • switch.wasser*
  • vacuum*

Recorder Mgmt and default ‘best practices’ is non-existent.

IMO there should be a dedicated page under our “new” Tools section (yes, sarcasm) that allows for setting Rules (shipped with default rules based on best practices) which automatically adds excludes globs most people would not need history for.

I’ve been using H.A.C.A to mange the Recorder excludes, it does a really nice job of telling you what may need excluding, and automating the process. The rest of the app is excellent in every other way also.

Here is an update to my previous post Martin84.

I now do believe, that there is a bug somewhere in the integration, because when I

  • deleted the gui-recorder integration
  • deleted my “inclusions” from my config.yaml (i.e. record everything)
  • restarted HA
  • installed gui_recorder,

everything works like advertised.
All sensors were initially recorded and I set the exclusions through the gui_recorder integration.

So I am assuming the integration expects only “exclusions” and somehow had a hick-up with my setup?

Lots of good activity here, catching up on a few threads:

@Frank_Beetz — glad the db_url fix is working on your setup. Separately, saw your other report about include: not behaving as expected — answered in detail on GitHub, quoting here for anyone following along:

Here’s what’s happening: your config has an include: block, which switches Home Assistant’s recorder into allowlist mode — only entities matching include get recorded at all, exclude just carves out exceptions within that. The per-entity toggle in GUI Recorder, though, only ever manages the exclude.entities list — it can remove an entity from exclude, or add it to exclude, but it never touches include. So when you toggle a new sensor “on,” the panel does exactly what it’s designed to do (make sure it’s not excluded) — but since it was never on the exclude list, and it’s also not matched by anything in your include block, nothing changes: the recorder still won’t record it, because inclusion is what actually gates recording when include is present.

Workaround for your specific sensor: the Manual exclusions field accepts include.entity_globs, and a glob with no wildcard characters just matches itself — so you can add the exact entity_id as a literal entry there and it’ll get included without needing a broader pattern.

Full writeup: Unable to record history with GUI Recorder running and indicating that sensor is included · Issue #15 · ideaalab/gui-recorder · GitHub

@EMqA44 — yes, a standalone recorder.yaml (included via recorder: !include recorder.yaml) is detected exactly the same way as an inline block in configuration.yaml — same code path either way, as long as GUI Recorder’s own include isn’t already active.

One caveat for your specific setup: if your ~100 entities are listed under include: entities: (a literal list), those won’t survive the import — only include.domains and include.entity_globs get carried over automatically. include.entities (allow-list mode, specific entity IDs) is the one thing that’s still genuinely dropped on migration; the panel calls this out during the process. If your config uses glob patterns instead of literal entity IDs, you’re fine. Worth checking before you migrate.

@CaptTom — really glad it’s useful, and thanks for flagging the DB size mismatch. Not a bug: the number we show sums the main .db file plus the .db-wal and .db-shm sidecar files SQLite uses in WAL mode. The -wal file in particular tends to stay “puffed up” at whatever size it grew to until a full checkpoint happens, even after your data shrinks — so it’s normal for the total to be noticeably bigger than what a file browser shows you for just the main .db file. Running Repack (VACUUM) forces a checkpoint and should bring it back in line.

On collapsible sections: good suggestion, putting it on the list for the next round of panel changes.

@Ltek — appreciate the kind words. The “shippable default best-practice rules” idea is really more of a Home Assistant core ask than something for this plugin specifically — GUI Recorder is intentionally just a GUI over the recorder’s existing config surface, not a rules engine. But noted, and glad H.A.C.A’s exclude suggestions have been useful for you in the meantime.

@meyerpr (also opened an issue, cross-posting here in case it’s relevant to others) — reported purge silently doing nothing on Docker. If anyone else hits this: the fastest way to tell if it’s a GUI Recorder problem vs. a Home Assistant/Docker environment problem is to call recorder.purge directly from Developer Tools → Actions — GUI Recorder’s purge button is just that same service call under the hood, so if it fails the same way there, it’s a HA/Docker permissions issue (UID/GID mismatches on mounted volumes are a common culprit) rather than anything specific to this integration.

Confirmed, and that matches exactly what’s explained above — not a new bug, the same limitation. Removing include: puts your recorder back in plain denylist mode, which is the only mode the per-entity toggle currently manages correctly. Glad the workaround got you unblocked.

If you ever want some of your original allowlist behavior back without losing panel control, the trick from my earlier reply still applies: add specific entities as literal (wildcard-free) entries under include.entity_globs in the Manual exclusions field — that gets them into the allowlist without the plugin’s own toggle needing to understand include natively.

Thanks for the detailed explanations!

I’m afraid I do have one more suggestion: The database records are presented in two sections: (1) by device, and then (2) by non-device entities. This was never the way I looked at it. I’d much rather have one list, by entity ID. Maybe sortable by device or entity ID.