Tag: Google

  • My Home Server Has Found Its Voice

    My Home Server Has Found Its Voice

    Until this week, everything my Raspberry Pi did was silent. It wrote notes, filled in dashboards, sent the odd email and scrolled messages across a little LED panel on my desk, but if I wasn’t looking at a screen I didn’t know about any of it. Now it can talk.

    On 29 October Claude Code and I built two small things: a command-line tool that plays speech or audio on the Google speakers around the house, and a Voice switch on my LED display’s control page that reads out whatever the display is showing. The speech itself is generated on the Pi, not by a cloud service. This post is how it fits together and the few things that tripped us up.

    Why bother?

    I already had the speakers, and I already had a lot of automations producing small bits of useful information: the next train, which bin goes out, what’s flying overhead. Reading them means looking at something. Hearing them doesn’t.

    I started it the way I start most things now: I dropped a task note into my vault’s Inbox asking for a way to play audio on the Google speakers. Before anything was built, I made three choices:

    • Both speech and audio files, not just one or the other.
    • The office speaker as the default, since that’s where I am when I’m working on this stuff.
    • Local text-to-speech rather than the Google Translate voice that Home Assistant already had set up. I’d rather the words my scripts generate weren’t sent off to be turned into audio somewhere else.

    The pieces

    Most of the work turned out to be already done. Home Assistant was running and already knew about every speaker in the house, so the job was mainly joining things up:

    • Home Assistant’s Google Cast integration exposes each speaker, and each speaker group, as a media player that can be told to play something.
    • Piper, an open-source text-to-speech engine, runs in its own Docker container on the Pi. It uses the British English en_GB-alba voice.
    • The Wyoming integration connects Home Assistant to Piper, which gives Home Assistant a new text-to-speech engine to use instead of the cloud one.
    • Home Assistant’s REST API is the single way in. Anything on the Pi that wants to speak makes one API call that says “say this, on that speaker”, and Home Assistant gets Piper to render the audio and tells the speaker where to fetch it.

    The access token for that API lives in a locked-down file on the Pi, like every other secret in this setup, and is read at run time.

    SpeakerCast: one command

    The first piece is SpeakerCast, a bash script of about a hundred lines wrapped around that API. It does three things:

    • speakercast say "…" speaks some text, on the office speaker unless I name another speaker or group.
    • speakercast play plays an audio file or a URL. A local file is copied into a folder Home Assistant serves on my home network, so the speaker can fetch it, and anything in that folder older than a week is tidied away on each run.
    • speakercast list shows every speaker and group Home Assistant knows about.

    There’s an option to set the volume first, and a dry-run option that prints the calls it would make without making a sound. That last one matters more than it sounds: like every service on the Pi, SpeakerCast has a regression test, and a test that runs regularly and says “test” out loud in my office would not last long. The test checks everything short of actually playing anything.

    It was built after midnight, so the only thing we couldn’t check that night was the bit you can hear. I ran the audible test the next morning, a “hello” and a local MP3 on the office speaker, and both played.

    The one gotcha

    Playing a local file needs Home Assistant to serve it over its /local/ web path, which comes from a www folder in its configuration. The catch is that Home Assistant only notices that folder if it exists when Home Assistant starts. Creating it isn’t enough; it needed one restart. Because a restart takes down the things that depend on Home Assistant, Claude waited until after my daily note had been created for the next day before doing it.

    Giving the LED display a voice

    The more fun half came that evening. On my desk there’s a Pi Zero with a Pimoroni Unicorn HAT HD, a 16×16 LED matrix that scrolls messages, with a web control page that’s part of my RailData server. It has a set of quick-send buttons that each fetch something and scroll it across the panel.

    That page now has a Voice Off/On switch. With it on, anything I send to the panel is also spoken on the office speaker:

    • any message I type in
    • the next trains
    • the next bin collection
    • the aircraft overhead (the feed behind it was the subject of last week’s post)
    • the AuroraWatch status
    • command words like “time” and “date”

    The switch starts Off, and the browser remembers it. Automated messages and the background animations stay silent, so the speaker only talks when I’ve asked it to.

    It uses exactly the same route as SpeakerCast: one Home Assistant call with the Piper engine. If the speaker call fails, the error shows on the page, but the message still goes to the panel. The display was there first, so speech is never allowed to break it.

    Writing for the ear, not the eye

    Text written for a 16-pixel-high panel is full of shorthand, and Piper reads exactly what it’s given. So the server rewrites each message before it’s spoken:

    • Command words become sentences. On the panel, “datetime” is drawn by the Pi Zero itself, so the server has to work out what to say, something like “It’s 20:17 on Thursday 29 October.”
    • Shorthand is expanded. The dots that separate items become pauses, arrows become “to”, “ft” and “mi” become feet and miles, “exp” becomes “expected”, and compass points like NE become “north east”.
    • Several messages become one sentence. Trains and bins send more than one message at a time. Speaking each one as a separate call doesn’t work, because the second call cuts the first off mid-sentence, so they’re joined into a single utterance.

    That last one is easy to miss. On a screen, two messages one after the other are fine; out loud, one stamps on the other.

    A pomodoro timer that tells you when to stop

    The display also has a pomodoro timer mode, and later the same evening it got a voice too. With Voice on, I hear “Pomodoro started: 25 minutes” at the start, the time left when I pause or resume, “Pomodoro stopped” if I give up, and at the end “Pomodoro complete. Your 25 minutes are up, time for a break.”

    This one needed a bit more thought, because the timer runs on the Pi Zero, not on the server. If the server tried to work out when 25 minutes were up, it would get it wrong as soon as I paused. So the Pi Zero now publishes an event over MQTT each time the timer changes state, and the server, which was already listening to the display for its live preview, picks up those events and speaks them. The finish is timed by the display itself, so it’s right after a pause, and it’s announced even if I’ve closed the control page.

    We tested it with a one-minute timer: the start and the finish both played on the office speaker.

    What I’ve learned

    • Check what you already have. Home Assistant already knew every speaker in the house. The whole thing was a container, one integration and a hundred lines of bash.
    • Keep the speech local. Piper runs in a small container next to everything else, and the text my scripts generate isn’t sent to a cloud voice service to be turned into audio.
    • Make it opt-in and quiet by default. The switch starts Off, the tests never make a sound, and nothing automated speaks unless I’ve turned it on. A house that talks without being asked would get switched off quickly.
    • Design for the ear. Shorthand, symbols and back-to-back messages all work on a screen and all fail out loud.

    It also gives Claude a new way to reach me: I’ve told it that when an interactive session needs me, it can ask to announce that on the office speaker rather than waiting for me to notice the terminal.

    A note on how this post was written: it was drafted by Claude Code from the SpeakerCast and RailData repos, their code history and the notes in my Obsidian vault, and written into WordPress as a draft through the WordPress API. The header image was generated with Google’s Gemini image model. I then reviewed and edited it before publishing.

  • What Did “Ross Goodman” Do?

    Ross Goodman
    Ross Goodman

    Is this the Ross Goodman you are looking for?

    I don’t know what happened but yesterday this blog saw a spike in traffic. It normally averages less than 80 views per day but yesterday – 539.

    Screenshot 2015-06-11 at 11.42.39

    I have done some quick analysis:
    Most of the traffic is coming from Google.
    About 200 views hit my home page rather than a specific post, the others are on a variety of posts – no hotspot there.
    The search terms used are no different from other days.

    All I can surmise is that either another Ross Goodman (there are a few of us) has done something and lots of people have found this site or Google have changed an algorithm somewhere and I’m benefiting from it.

    Let me know if you have any ideas!

  • How Old Are My Google Contacts?

    It may be a sign of old age but, I’m struggling more and more to remember how old people are. I have addressed this by having a review of my Google contact data and making sure, where I know someone’s birthday I have recorded it there.

    This has the added advantage of creating a Google calendar showing me their birthdays.

    I have now created a script which scans my contacts to find their birthdays, does some arithmetic to work out their age and store this in a custom field. I have set up a trigger to run this once a week.

    Here is the script that I created:

    (more…)

  • Review a random Google Contact every day

    I have all of my contacts loaded into Google Contacts but the problem is, I’m never convinced that they are up to date or even sure if I should still have the person’s contact details listed.

    To address this I created the following script which will send me an email every day with a randomly selected contact.

    I can then review the contact’s details, compare it with LinkedIn, Facebook and Twitter and then either update their contact details or send them an e-mail to catch up with them.

    (more…)

  • I’m Back – And Faster !

    Well it’s September now and this is my second post of the year!

    It’s been a busy year but no excuses – I have neglected my blog. To make up for this I treated my blog to a new host. Gone are PlusNet and in are TSOHost.

    I can’t recommend TSOHost highly enough. Within hours of me sending my backup to them they had migrated my blog and it was up and running and it’s MUCH faster! More importantly it’s back onto a supported software stack and it’s patched and up to date !

    I have stacks of ideas for blog posts; specifically a series on using Tasker on android phones to send data to web forms , collating and analysing data in google spreadsheets then using web services to send data back to tasker to display a dashboard on your phone!

    As an added bonus – previously some of my posts were password protected – these are now free and open!

    Enjoy

  • Contacts – Keep In Touch or Update Details

    One of my New Year resolutions was to keep in touch with more people.

    I also need to clear out my contact list as there are lots of “old” and incomplete entries in my Google contacts list.

    Ever the productivity geek I wanted to automate this. I ended up writing the following Google Apps Script:

    (more…)

  • InBox Zero And Quantified Self

    I’m a huge fan of keeping my inbox empty, but not very good and keeping it that way.

    I’m becoming more interested in the “Quantified Self” concept i.e. you can only fix things that you can measure.

    The obvious answer is to monitor what my GMail inbox looks like on a daily basis. (more…)

  • Social Media – How I Make It Work

    Social Media – there are so many sites out there all wanting my input.
    There are so many other web sites that produce content that want me to read their content.

    You can see what my plumbing looks like here.

    (more…)

  • Google Spreadsheet & SQL Queries

    Learning SQL
    Creative Commons License photo credit: therefromhere
    I am a big fan of Google Spreadsheets, they give you most of the features of a desktop “office” based spreadsheet but your data is stored online, no more need to carry your data on a USB stick.
    I’m also a huge fan of databases, SQL is a fantastic tool for querying data.
    Now you can combine the best of both worlds: store your data in a spreadsheet and then summarise the data using SQL.
    (more…)

  • Google Calendar Sync – Auto Run

    A nice quick hint.

    I kept forgetting to start Google Calendar Sync when I started Outlook. I don’t always start Outlook when I start my laptop and I’m not always online when I run Outlook so puting it in my Startup folder is not really an option.
    My solution, a macro that runs when Outlook starts that gives me the option of running Google Calendar Sync if I feel it’s appropriate.

    Private Sub Application_Startup()
    Dim response As Integer
    Dim RetVal
    op
    response = MsgBox(prompt:=”Do you want to run Google Calendar Sync?”, buttons:=vbYesNo)

    If response = vbYes Then
    RetVal = Shell(“C:\Program Files\Google\Google Calendar Sync\GoogleCalendarSync.exe”, vbMinimizedNoFocus)
    End If

    End Sub

    NOTE: WordPress seems to “prettyfy” the quotes so when copying the code above it is better to delete and retype the quotation marks in your code editor of choice.