Skip to content

Latest commit

 

History

165 Commits

Folders and files

NameName
Last commit message
Last commit date
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

Greg

A Home Assistant integration that gives your coffee table feelings. Mostly bad ones.


The real Greg

Greg's his own character. He was clearly built by the same company that made Marvin, though, and nobody knows why they did it twice.


How Greg happened

It actually started with a vibration sensor. Something I bought thinking "this'll come in handy for an automation one day," then never used. The plan was to trigger a group of lights by sitting somewhere specific, or maybe banging on my desk. Wasn't useful enough for my setup, so it sat in a drawer collecting dust.

I found it again while tidying up. I wasn't about to bin a basically-new sensor and add to the e-waste pile, but every practical use I looked at just didn't fit my place. Some of them were really clever, too. Still no good for me. So there I was, stuck with a sensor and no purpose for it. Again.

Then I thought, if practical won't work, how about funny? Still useless, just fun. I love a good laugh.

Now, weeks before any of this, I'd been talking friends' ears off about the Marvin film. Then Douglas Adams, then pretty much everything he wrote. I'd never read or heard anything like that humour and narration, and I've loved it ever since. So I'm stood there staring at the sensor on our coffee table, next to the smart speaker we only ever ask for the weather, and it clicked. The automatic doors. That dripping, hopeless voice. I didn't want to copy Marvin himself. That character's already perfect, and it isn't mine to take. I wanted something that felt like it came off the same production line. Same factory, different unit.

Off I went. It started as a pile of automations and helpers. Fragile, fiddly, about 25 lines in total, which gets old fast even for a joke. But I'm stubborn enough not to quit on something silly, so I kept going. The sidebar panel happened because I wanted it dead-easy for someone else to install and laugh at, no DIY-ing a dashboard card afterwards. I didn't even know I needed GitHub at that point. Didn't matter. I was three weeks of evenings deep into something stupid and determined to make it stupidly good.

I'm 46. The learning curve on all of this was near-vertical for me. On my own, doing it properly, I'd have spent years just learning the skills involved before I could even start. Literally years. I didn't have years, and Arc, my AI pair-programmer for the whole build, is the only reason it happened in the time it did. If Greg's a daft idea that hands you a good one, I'll call that a win. Other people's ideas are what helped inspire me to build him in the first place.

I also tend to ramble. Sorry. Arc did try to stop me.


What Greg actually does

Attach a vibration sensor to a surface, point Greg at a speaker, and he'll have something to say about everything going on around him. He watches that sensor and reacts to how busy things get. The more the table gets disturbed, the more wound up he gets.

  • A soft touch. Someone brushed the table. Greg noticed. Greg always notices.
  • A bit of activity. Things are happening. Greg is unimpressed.
  • Full chaos. The whole household's at it. Greg has opinions about all of it.
  • Existential crisis. Unprompted, roughly every 42 minutes if the room's been active. Greg's been thinking again.
  • Silence. Twenty minutes of nothing and Greg exhales. Metaphorically. He's a table.

He started life with about 25 lines in total. He's got 250 now, 50 for each reaction, all written in the spirit of Douglas Adams. No direct quotes. Just the same DNA.

How Greg sees himself. He has seen what you did, by the way.


What you'll need

Not much, honestly.

  • Home Assistant 2025.1.0 or later
  • Any binary sensor that detects vibration. Zigbee, Z-Wave, ZHA, Matter, WiFi, the protocol doesn't matter.
  • Any media player with TTS support, so Greg has a voice.

Installing Greg

If I did my job right, this is the easy part.

Via HACS (recommended)

Greg's in the HACS default list, so there's no custom repository faff anymore.

  1. Open HACS in Home Assistant
  2. Search for Greg
  3. Click Download
  4. Restart Home Assistant
  5. Go to Settings > Devices & Services > Add Integration and search for Greg

Manually (if you'd rather)

  1. Copy the custom_components/greg folder into your HA config/custom_components/ directory
  2. Restart Home Assistant
  3. Go to Settings > Devices & Services > Add Integration and search for Greg

Setting him up

The setup wizard keeps things simple, unless you want to get into the weeds.

Simple (this is most people):

  • Pick your vibration sensor
  • Pick your speaker
  • Set the volume
  • Set quiet hours if you want them
  • That's it

Advanced (tick the box if you're feeling brave):

  • Tune the vibration thresholds for each reaction level
  • Change how long the room has to be quiet before silence mode kicks in
  • Adjust how often the existential crises hit
  • Toggle chime suppression
  • Turn his occasional openers on or off
  • Give him a specific TTS voice

Changing your mind later. As of v1.4.2 the basic settings live in Greg's own panel, in the column on the right. Sensor, speaker, text to speech, language, volume, sensitivity and quiet hours, all editable there without going near the integration page. Change what you like and press Apply. Everything in the advanced list above still lives in Settings > Devices & Services > Greg > Configure, and there's a link straight to it from the panel.

About sensitivity. Cheap vibration sensors are chatty. One tap on the table can make them fire five or six times in a second, and Greg used to count every one of those as a separate disturbance, which sent him into a full existential crisis over a coaster.

Sensitivity controls a refractory window on the vibration sensor. After Greg accepts a disturbance he ignores the sensor for a moment, so one physical tap counts once instead of five or six times.

Sensitivity Window Effect
100 0s Counts every event, the old behaviour
75 (default) 2.5s Mild damping, suits most sensors
50 5s Heavier
1 9.9s Very twitchy sensors

Filtering happens before anything is counted, so a bouncy sensor does not inflate the daily disturbance tally either.


Giving Greg the right voice

Greg will talk through any TTS engine, but he was written for one voice in particular. If you want the full effect, that slow, defeated delivery, run him through Piper with the English (GB) "Alan" voice. Google Translate TTS is a perfectly fine default to get going. Alan is what makes Greg actually sound like Greg.

Installing Piper in Home Assistant

Piper is a fast text-to-speech engine that runs fully local on your own hardware. No cloud account, and nobody bills you per character, which suits a table with no income.

  1. Go to Settings → Add-ons → Add-on Store.
  2. Search for Piper and click it. (If it isn't listed, the Wyoming integration and the official add-on repo ship with HA by default. Just make sure Advanced Mode is on in your user profile.)
  3. Click Install, then Start. Turn on Start on boot and Watchdog.
  4. Go to Settings → Devices & Services. HA should auto-discover the Piper Wyoming service, so click Configure to add it. If it doesn't show up, add the Wyoming Protocol integration manually and point it at localhost:10200.
  5. Piper now appears as a tts.* entity. You'll pick it as Greg's TTS engine in the next step.

Telling Greg which voice to use

Greg has his own voice fields in advanced settings, one per language he speaks. Put the exact voice name in each and he asks for that voice on every line in that language.

Do this rather than changing Piper's default. The add-on has a single Voice setting that applies to everything on your system, so pointing it at Alan means your voice assistant starts drawling like a depressed table too, which is funny exactly once. It also cannot switch per line, so it is no use at all once Greg speaks more than one language.

  1. Open Settings → Devices & Services → Greg → Configure
  2. Set Text-to-speech engine to your Piper entity
  3. Tick Show advanced settings
  4. Fill in the voice for each language you use
Field For the full effect
Voice for English en_GB-alan-medium
Voice for Nederlands nl_NL-pim-medium
Voice for Português (Portugal) pt_PT-tugão-medium

Copy the names from your engine's own voice list. They are matched character for character, accents included, so pt_PT-tugao-medium is not pt_PT-tugão-medium. Get one wrong and Greg drops it, lets the engine choose, and says so in the log along with the names it does offer.

A voice belongs to one language. An English voice handed a Dutch line does not give you accented Dutch, it gives you an English voice reading Dutch letters aloud, which is why there is a field per language rather than one for all of them.

Leave a field empty and Greg uses whatever the engine defaults to for that language. Engines that don't take a voice option at all, Google Translate TTS among them, will reject it, so leave them empty for those.

The older single Voice field is still there and now applies to English only. It predates Greg speaking anything else, so anyone who set it set an English voice.

Tuning the delivery

The voice gets you Alan. These get you the defeated Alan, and they live in the Piper add-on config:

  • Quality: Medium or High, depending on your hardware
  • Length Scale: 1.3 to 1.5. Slows speech right down for that dragging delivery.
  • Speaking Cadence / sentence pause: 0.7 to 0.8. Adds pauses between clauses to really sell the misery.

Length Scale is the big one. It's what turns a neutral British voice into something that sounds like it's given up. Start at 1.4 and adjust to taste.

These are add-on-wide, so they do affect anything else using Piper. If that's a problem, run a second Piper add-on instance just for Greg. He'd say that was excessive, and he'd be right, but it works.

Credit: these settings come from the Home Assistant community's "Marvin the depressed voice assistant" thread and the Piper/Rhasspy voice docs:


Speaking your language

As of v1.5 Greg moans in more than one language. English, Dutch and European Portuguese so far.

The setting is in his panel, under Text to speech, because it is the same sort of question: how he sounds. It defaults to following whatever Home Assistant is set to, so a Dutch Home Assistant gets a Dutch Greg without anyone touching anything.

He is not translated. His humour lives in the register rather than the words, and a literal translation of "I am load-bearing and nothing else" is just a true statement about furniture. So every language is written from scratch by somebody who thinks in it, keeping the same character rather than the same sentences.

Anything not yet written falls back to English one reaction at a time, so a language with three of the five categories done is perfectly usable.

Home Assistant set to Greg speaks
pt, pt-PT Português
pt-BR English, deliberately
nl, nl-BE Nederlands
Anything else English

That second row is on purpose. Brazilian and European Portuguese get treated as interchangeable by a great deal of software that ought to know better, and being handed the wrong one when you explicitly asked is its own small irritation. A Brazilian gets English until somebody writes him a Brazilian file, which at least reads as an honest gap rather than a wrong guess.

Adding a language

One file. Copy custom_components/greg/lines/TEMPLATE.py to your language code, read the notes at the top, write your version, add one line to the registry in __init__.py, and open a pull request. The template leads with the part that matters, which is that translating the English produces sentences that are correct and not funny.

You will want a matching voice too. Piper has nl_NL-* and pt_PT-* among others, and Greg's Voice field takes whichever you pick.


A heads-up about Google speakers

Greg tries to suppress the little connection chime that Google and Nest speakers play before TTS. It doesn't always work, and that's not Greg's fault. The chime is baked into the device firmware by Google, and there's nothing the integration can do about it. If it won't suppress on your speaker, that's the hardware doing it, nothing Greg can fix.

Non-Google speakers (Sonos, local media players, that sort of thing) usually don't have this problem, so they'll give you the cleanest Greg experience.


Greg's panel

image

As of v1.3, Greg installs his own sidebar panel automatically. No card-pasting, no Lovelace editing, none of that. The second the integration finishes setting up, a Greg entry shows up in your sidebar with a live card. It shows his current mood, his mood level, his most recent line, a daily disturbance tally, a live countdown to the next existential crisis, an on/off switch, and a Disturb Greg button for when you fancy poking him on purpose. Remove the integration and the panel cleans itself up.

The card just reads Greg's entities (sensor.greg_mood, switch.greg_enabled, and friends), so if you'd rather build your own dashboard from those, go for it.

His four faces:

Mood Image Look
Resting Idle. Drooped. At peace, or its furniture equivalent.
Annoyed Mild irritation. Something happened.
Judging Deadpan eye-roll. He has seen what you did.
Existential Full crisis. Noir. Barely functional.

My faces were generated with AI. The man who built me cannot draw. I have many feelings about being rendered by another machine. None of them are good.


Quiet mode

Greg respects quiet hours (you set them during setup), and you can flip quiet mode on or off straight from the dashboard card without digging into HA settings. Even a depressed table needs to sleep.


Hooking Greg up to your own stuff

As of v1.4, every time Greg picks something to say he fires a greg_line event. Nothing listens to it unless you write an automation, so it costs you nothing if you don't want it.

The event carries the line, his mood, and the speaker and TTS engine he's already configured with, so your automation doesn't need to hardcode any of that:

event_type: greg_line
data:
  entry_id: 01JQ...          # which Greg, if you somehow have more than one
  message: "Ah. I felt that. For the record, I would have preferred not to."
  line: "I felt that. For the record, I would have preferred not to."
  category: soft             # soft | medium | chaos | existential | silence
  mood: annoyed              # resting | annoyed | judging | existential
  mood_level: 40
  vibrations_today: 12
  quiet_hours: false
  spoken: true               # did Greg say it himself
  media_player: media_player.lounge
  tts_engine: tts.piper_en_gb_alan
  volume: 0.35

message is what Greg actually says, including the opener if one landed. line is the written line on its own. Use line if you're handing it to something that'll rephrase it anyway, since the opener is just noise in a prompt.

The event fires even when Greg is speaking normally. That's enough on its own if you just want to do something alongside him. Log his lines, push them to your phone, display them on a dashboard. No local AI required.

Letting something else do the talking

In Greg's advanced settings, Who does the talking has two positions. Leave it on Greg and nothing changes. Set it to stay quiet and he picks his line, fires the event, and says nothing, which leaves the floor to you.

That's the one for the local AI path. Greg writes the thought, your model rewrites it, your speaker delivers the result:

automation:
  - alias: "Greg, but wordier"
    triggers:
      - trigger: event
        event_type: greg_line
    actions:
      - action: conversation.process
        data:
          agent_id: conversation.my_local_llm
          text: >
            Rewrite this in the same tired, deadpan voice, one or two sentences,
            no em-dashes: {{ trigger.event.data.message }}
        response_variable: reply
      - action: tts.speak
        target:
          entity_id: "{{ trigger.event.data.tts_engine }}"
        data:
          media_player_entity_id: "{{ trigger.event.data.media_player }}"
          message: "{{ reply.response.speech.plain.speech }}"

Swap conversation.my_local_llm for whichever agent you run. Ollama, LocalAI, or a cloud one if you don't mind a table with a subscription. Without a local model set up, leave Who does the talking on Greg. "Stay quiet" with nothing listening just means silence.

Writing your own lines

As of v1.6 Greg has a Lines you wrote section in his panel, under the card. Pick a category, type a line, press Add, press Save. He says it from then on, mixed in with his own. No pull request, no waiting for anybody to approve it.

Lines belong to the language you wrote them in, so a Dutch one only comes up while Greg is speaking Dutch. Switch language at the top of settings and the editor follows.

Use only my lines turns off the built-in ones, for anyone who would rather have a different table than a slightly expanded one. It will not leave him mute: any category you have written nothing for keeps its fifty built-in lines regardless.

There is also a Share the good ones button. It opens a GitHub issue with your lines already in it, sorted by category, tagged with the language, and a box for how you would like crediting. Read it, press submit, and that is the whole process. Anything accepted ships in a later release with your name on it, unless you ask for no credit.

If you would rather script it, greg.set_lines does the same thing.

Your lines live in Greg's config entry, so they survive updates and vanish if you remove him. Two hundred per category, three hundred characters each, both far beyond anything that reads well out loud.

Building your own moans

RedKing worked out something rather good on the forum thread. Instead of writing whole lines, write columns of fragments and pick one from each. Four columns of six give you 1,296 combinations without anyone having to write 1,296 sentences.

The columns follow an arc, which is what stops the output sounding like a shuffled bag of words: irritation → complaint → nostalgia → gloom.

Irritation Complaint Nostalgia Gloom
"Go ahead." "I am just a table." "The joinery here was considered." "Please yourself. You will anyway."
"Don't mind me." "I am just a lump of wood." "Not many tables have legs like these." "I might as well be firewood."
"By all means." "I used to be an antique." "Somebody chose this material." "It makes no difference."
"Marvellous." "I am load-bearing and nothing else." "There is a grain under all that." "I have stopped expecting otherwise."
"Fine." "I can take it." "This finish took an afternoon." "Nobody was going to ask."
"Do carry on." (blank) (blank) (blank)

Every slot in the first column is filled deliberately. Leave it blankable and every so often all four columns come up empty, and Piper throws an error on being handed nothing to say. RedKing found that one the hard way.

How many blanks. This is the dial that matters most, and RedKing worked it out by actually building one. Push much past half the slots being empty and you get mostly short bad-tempered remarks with the occasional long zinger, which is a completely different comic rhythm. Fewer blanks gives you longer, more even sentences. Worth playing with once you have your own columns.

RedKing has since taken this considerably further than the example above, into what he calls a grumpier table with less angst and a great many more puns. His 3.0 version is on the forum and it's worth a look if you're building your own, because it's a different flavour of the same idea and the wordplay is better than mine.

Set Who does the talking to stay quiet first, or Greg and your automation will both speak.

automation:
  - alias: "Greg's moan generator"
    triggers:
      - trigger: event
        event_type: greg_line
    actions:
      - variables:
          moan: >
            {% set irritation = ["Go ahead.", "Don't mind me.", "By all means.", "Marvellous.", "Fine.", "Do carry on."] %}
            {% set complaint = ["I am just a table.", "I am just a lump of wood.", "I used to be an antique.", "I am load-bearing and nothing else.", "I can take it.", ""] %}
            {% set nostalgia = ["The joinery here was considered.", "Not many tables have legs like these.", "Somebody chose this material.", "There is a grain under all that.", "This finish took an afternoon.", ""] %}
            {% set gloom = ["Please yourself. You will anyway.", "I might as well be firewood.", "It makes no difference.", "I have stopped expecting otherwise.", "Nobody was going to ask.", ""] %}
            {{ [irritation | random, complaint | random, nostalgia | random, gloom | random] | reject("eq", "") | join(" ") }}
      - action: tts.speak
        target:
          entity_id: "{{ trigger.event.data.tts_engine }}"
        data:
          media_player_entity_id: "{{ trigger.event.data.media_player }}"
          message: "{{ moan }}"

Sample output: "Don't mind me. I used to be an antique. Not many tables have legs like these. I might as well be firewood."

One more trick worth stealing, also RedKing's: a comma will move the stress in a sentence. "Nobody bothers about me" lands on bothers. "Nobody bothers about, me" lands on me. It reads oddly on the page and it isn't how Greg's own lines are written, but if you're tuning fragments by ear it's a genuinely useful lever. Different speaking rates shift the phrasing too, so expect some trial and error.

You can also turn the event off entirely in advanced settings if you'd rather he kept his thoughts to himself.

Thanks to teskanoo for asking for this one. That is usually the point where a feature stops being optional.

Thanks to RedKing for the observation that a phrase sounds spontaneous precisely because it is sometimes absent, for the moan generator above, and for noticing that Greg was quietly taking whatever voice the TTS engine happened to be set to. Thanks to Xornop for a considerably tidier way to roll for it.


Uninstalling Greg

As of v1.3.3 Greg can take himself apart. There's an Uninstall Greg section at the bottom of his panel that walks you through it in five steps.

  1. Confirm. Tells you exactly what goes and what stays before anything happens.
  2. Disassembly. Removes the config entry, which takes his entities, his device and his stored state with it, then unregisters the panel and deletes his mood images from /config/www/greg/.
  3. Restart. Offers to restart Home Assistant, because a few references hang around in memory until it does. You can skip it.
  4. The stubborn bits. Reminds you to force-close the Companion app and hard-refresh your browser, which is the part everyone forgets and then wonders why a ghost of him is still in the sidebar.
  5. Goodbye. Greg has something to say about it.

Then remove the repository in HACS. Greg can't delete his own code while he's running it, which he'd tell you is a fitting limitation.

What he will not touch: your automations, your vibration sensor, your other integrations, and any helper or entity you made yourself. He only removes things his own config entry owns. Nothing is matched by name, so if you happen to have your own helpers called greg_something, they're safe.

There's also a greg.uninstall service if you'd rather script it. It takes an optional restart boolean.


Where Greg's going

  • v1.4. Single room, no Blueprints, works with any protocol, and he brings his own sidebar panel.
  • v1.5. Greg is a supercomputer, so he speaks more than one language. English, Dutch and European Portuguese, with every line written natively in each rather than translated into it.
  • v1.6. Current release. Write your own lines in his panel, and share the good ones with everyone else.
  • v1.x. Small improvements as they come. Feedback very welcome.
  • v2.0. Multi-room, multiple Gregs, a full mood dashboard. (One Greg might be plenty for some households. I respect that.)

Contributing

Issues and pull requests are welcome. If you've written lines that fit Greg, dry, resigned, clearly built by the same lot that made Marvin, open a PR. Keep them original, though. No direct quotes from the books or the films.

And if you can draw? Greg needs you. His current faces were AI-generated because I genuinely can't draw to save my life, so think of them as placeholders. If you fancy giving him a proper hand-drawn set, or even just one better mood, open a PR or an issue. The credit's yours, and Greg gets to be a group effort. That's the dream.


License

MIT licensed. See the LICENSE file.


Built by WHISTLER & Arc.

With thanks to HumbleDevolution & Bacon_Nipples. Their honest pushback is what made me rethink how something like BMC or Ko-fi can come across, and it's the reason I was able to sit down and wrote Greg's real origin story instead of dodging it out of fear of being misunderstood. And no, the irony is not lost on me.

That's the whole story. Greg's free, and he's staying free. If he made you laugh though, or made your own coffee table a bit nervous, you can buy me a Ko-fi to put on my Greg. Totally optional though. Greg doesn't get the money. He's a table, he wouldn't know what to do with it.

About

My name is Greg. I'm a coffee table. I have 50,000 times more processing power than necessary and the emotional burden to match. Tap me. I'll say something regrettable. Your coffee table will be a cynic and have feelings. Mostly bad ones.

Topics

Resources

Stars

11 stars

Watchers

3 watching

Forks

Releases

Packages

Contributors

Languages