Debugging a Fridge

No, I am not referring to some new IoT wifi smart fridge of the future. As a Software Engineer, I like applying the same problem solving techniques I use in my day job to other domains – like fixing fridges!

Recently, our fridge started giving problems – it just never cooled down and kept running. The first suspect was the door rubber seals. The door seemed to have drooped slightly over the years, but the seals were ok.

Next, I checked the ventilation. I threw out some preserves that were old and moldy and repacked everything to assist the air flow. I places a thermometer inside so I could monitor improvements.

The fridge purge didn’t have much effect. Then I realized something – because of the door drooping slightly, the switch that controls the interior fridge light was no longer triggered when the door closed because it is mounted above the door.

But why would the light switch affect the cooling of the fridge? I figured that modern, energy efficient fridges would likely want to disable the interior fan that circulates cold air inside the fridge when the door opens, preventing the cold air from escaping.

A quick test by taping the switch in closed position confirmed that this was indeed the problem.

I attached a small spacer to the top of the door with double sided tape to accommodate for the door droop so that the switch is once again triggered when the door closes.

For now, the fix will do. Time to start saving for a new IoT wifi smart fridge! 😀

The Biggest Python Trap!

If I had a Dollar for every hour I’ve wasted debugging this issue in Python, I’d be … uhh… well a little more well off than I am now.

The way that Python handles modules is fantastic, but sometimes if you don’t pay attention, you can end up wasting time debugging an issue when the error message leads you down the wrong path.

Often – and this blog is probably more a reminder for myself than anyone else – I create small test scripts to play around with new modules. In the most recent example, I started experimenting with Kivy – the Python UI library.

I created a script called “kivy.py” and copied the hello world example code and ran it.

Traceback (most recent call last):
  File "kivy.py", line 1, in <module>
    from kivy.app import App
  File "C:\Python\hal\kivy.py", line 1, in <module>
    from kivy.app import App
ModuleNotFoundError: No module named 'kivy.app'; 'kivy' is not a package

Of course, the first thought is that the module was not properly installed. It could take some time (as is usually the case with me) to realize that the actual problem is that I named the test script (kivy.py) the same as the module I am trying to import:


from kivy.app import App
from kivy.uix.button import Button

class TestApp(App):
    def build(self):
        return Button(text='Hello World')

TestApp().run()

Silly, silly mistake, but somehow I do this ALL THE TIME. I think it’s time to start naming my scripts “trying_out_kivy.py” or something 😀

The Raspberry Pi Powered Speaking Doorbell – Part 3: Text to Speech

In Part 1 we looked at a simple input circuit to isolate our Raspberry Pi from our doorbell circuit and in part 2 we looked at making a camera overlay appear in Kodi. Next, we’ll look at building the text to speech server.

Please note that the following blog post uses code snippets from my Github project. You will need to clone or download the full source code to run the examples.

With my home setup, I have a dedicated media PC in the lounge which runs Kodi on Windows. It is connected to a Yamaha receiver which is permanently on. The doorbell circuit, however, is connected to a Raspberry Pi. In my case, it makes sense to have the audio for text to speech play over the media PC. But how do we trigger text to speech on the media PC from the Raspberry Pi when someone presses the doorbell?

To solve this problem, I built a simple text to speech handler using the Tornado Web Server – this web server runs on the media PC in the lounge. When the doorbell switch is pressed, the Raspberry Pi simply performs an HTTP request to the text to speech server, which then outputs the given text as speech over the Yamaha receiver.


from lib import handler
import pyttsx

class TextToSpeechHandler(handler.Handler):
    def post(self):
        text = self.get_argument('text')

        engine = pyttsx.init()

        engine.setProperty('rate', self._registry['config'].getint(
                    'text_to_speech.rate'))

        engine.setProperty('volume', self._registry['config'].getfloat(
                    'text_to_speech.volume'))

        voices = engine.getProperty('voices')
        for voice in voices:
            if voice.id.lower().find(self._registry['config'].get(
                    'text_to_speech.voice').lower()) != -1:
                engine.setProperty('voice', voice.id)

        engine.say(text)
        engine.runAndWait()

We define a handler “TextToSpeechHandler” which accepts HTTP posts and will convert an argument called “text” to speech. The handler inherits from my base Handler class (which contains some functionality which is common to all my handlers), which in turn inherits from the standard Tornado handler.

For text to speech, we’ll use the pyttsx package. I have made 3 parameters configurable here – The rate, which is how fast the text is spoken, the volume, and the voice to use (it performs a partial text match on the voice configured). I have the following configuration set up:

#System finds first voice ID that contains the below text, case insensitive
text_to_speech.voice = Hazel

#Speed of speech. 100 is "normal" speed
text_to_speech.rate = 130

#Volume. 1.0 is full volume, 0.0 is no volume.
text_to_speech.volume = 1.0

An example script that performs an HTTP post to the text to speech server:

from lib.bootstrap import Bootstrap
import requests

bootstrap = Bootstrap('default', ['config', 'log'])
registry = bootstrap.bootstrap()

text_to_speech_hosts = registry["config"].get('text_to_speech.hosts')

text = 'There is a visitor at the front door.'

for host_and_port in text_to_speech_hosts:
    url = 'http://' + host_and_port  + '/text_to_speech'
    payload = {"text": text}
    requests.post(url, payload)

We define the text we want to convert to speech in the “text” variable. Then, for all text to speech servers that are configured, we perform an HTTP post with the text as a JSON encoded string.

In the next part, we’ll look at some other utility libraries before digging into the actual doorbell code.