Thursday, February 2, 2023

The Magic Fridge Part 2

In my first post about The Magic Fridge, I talked about how I could use a Raspberry Pi to turn my garage fridge into a smart appliance. The first thing I did was set up a door alarm for when the door was open for too long. The reason I wanted this feature is because on this particular fridge if you slam the door shut, it will pop back open. So on many occassions I would walk into the garage to find the door open to the fridge.

The first thing I did was purchase a magnetic reed switch from Amazon. Here is an image of what that looks like:
If you are not very familiar with electronics, a magnetic reed switch is a simple switch for controlling the flow of electricity through the use of a magnet. You can get a good explanation here. To use this with my fridge, I attached one side to the side of the fridge and one side to the door. When the door is closed, the magnet closes the switch and thus completes the circuit. When the door is open, the magnet pulls away and opens the circuit which then prevents the circuit from being completed.
I then also wired up an LED bulb to the Pi to turn on when the door is opened. Here is a diagram that gives an overview of everything we have thus far.
Notice that in both circuits I added a small resistor. This is to minimize the current flow and protect the Raspberry Pi from damage. Now that we have our hardware configured, let's take a look at the code.

Step 1 is just to import the Python package that I'll be using:
from config import config
from gpiozero import LED, Button
from message import sendTextMessage
from time import sleep
Next I am going to set up a few things: I will use the max_count variable to be my threshold. So in this case, if the door is open more than 60 seconds, I will take action. Button and LED come from the gpiozero interface (which greatly simplies working with components on the Raspberry Pi in Python). This is just telling my code which pins my button or switch in this case and LED bulb are connected to respectively. The testing and verbose variables will just be debugging purposes.
max_count = 60
testing = False
verbose = False

switch = Button(17)
light = LED(26)
Next I set up a couple of functions to tell it what to do when the switch is open or closed. If I am debugging, it will print a message to the console and then either turn the LED bulb on or off as in each case appropriate.
def switch_open():
    if testing or verbose:
        print("Switch open")
    light.on()

def switch_closed():
    if testing or verbose:
        print("Switch closed")
    light.off()
Next, I set up the event handlers when_released and when_pressed to tell it what to do when the switch is opened or closed.
switch.when_released = switch_open
switch.when_pressed = switch_closed
And then comes the main code block. It is going to initialize my counter to 0, then perform an infinite loop. Each iteration of the loop will check to see if the switch is "pressed" or closed. If that's the case, we reset our counter and do nothing. But if the switch is open, we start counting. When our count reaches our threshold, we will start blinking the light: on for .2 seconds and then off for .2 seconds. And then it will send me a text message to let me know that the door is open so that I can go close it.
count = 0
while True:
    if switch.is_pressed:
        count = 0
    else:
        count = count + 1
        if verbose:
            print("Switch has been opened for %s seconds" % count)
    if (count == max_count):
        light.off()
        light.blink(.2, .2)
        message = 'Fridge door has been open for ' + str(max_count) + ' seconds'
        if verbose:
            print ('Send message to ' + config["alertPhoneNumber"] + ': ' + message)
        if not testing:
            sendTextMessage(config["alertPhoneNumber"], message)
    sleep(1)
Here is the content of my message package that I used for sending myself text message:
import os;
import requests;

def sendTextMessage(phone, message):
    apiKey = '*my-api-key*'
    resp = requests.post('https://textbelt.com/text', {
        'phone': phone,
        'message': message,
        'key': apiKey
    })
    json = resp.json()
    remaining = int(json["quotaRemaining"])
    if (remaining < 5):
        message = 'Less than 5 text messages left. Go to https://textbelt.com/purchase/ to purchase more.\n\nAPI key: ' + apiKey
        os.system('echo "' + message + '" | mail -s "*my-email-address*')
    return resp.json()
Finally, to kick off the script running, I added it to my crontab so that it will automatically run at startup.
@reboot /home/pi/python/fridge/door-check.py
One final note: In my message package above, I have it email me if my quota is getting low and I need to purchse more message on my SMS gateway. Unfortunately as of May of last year Google no longer supports this. So if you have a way of sending emails from your Rasperry Pi, please let me know.

The Magic Fridge

This is the first in what is hopefully going to be a series about The Magic Fridge. What is The Magic Fridge, you say? Well, I have a beverage fridge that I keep in the garage. I am always trying to find unique sodas and drinks to stock it with. My wife made a comment one day about how she never saw me stocking it yet it always seemed to have something new. She said "it's like some sort of magic fridge." The name stuck and now everyone affectionately refers to it as the magic fridge.

One day I got an idea to make it a bit more magical. I thought, what if I could use a Raspberry Pi to turn my ordinary beverage fridge into a smart fridge. Sure, I could probably just go buy a smart fridge (if I had the space and the money) but I have an engineering mindset and enjoyed the technical challenge of making it happen.

There are 3 main features that I implemented with on the Magic Fridge: My hope is to also write an app to track my inventory. I've done some preliminary work on that but as things go with side projects, it has been stagnant for a while.

Tuesday, June 16, 2020

isNaN is not the same as Number.isNaN

Since JavaScript is an untyped programming language, a variable can contain data of any type at any time. But sometimes you need to know what type the data is. Something I have done in the past, is use the isNaN function for this.

Take this (bad?) example. If the variable myData is a number, then we want to format it to 2 decimal places. Otherwise, we just want to display it as is. One way to do that would be:

if (isNaN(myData)) {
   displayString(myData);
else {
   displayDecimalNumber(myData);
}
But then along comes the linter complaining about isNaN:

So I blindly accept its suggestion and change it to Number.isNaN. But now there's a problem because now it's trying to format my string data as a number. What happened?

The problem is that the global isNaN does not behave the same way as Number.isNaN.

The global isNaN function converts the value to a Number, then tests it.

Number.isNaN does not convert the value and will return true for any value that is not of type Number.

So maybe a better way to write this code would be:

if (typeof myData === 'number') {
   displayDecimalNumber(myData);
} else {
   displayString(myData);
}


Wednesday, April 22, 2020

Implicit Argument to JavaScript Promise

I came across some code at work that I did not understand. After doing a little digging, I learned something I did not know about Promises. If you have a single argument to a function, you can use an implicit argument. What does the mean? Let's start with an example:
test(0)
 .then((results) => test(results))
 .then((results) => test(results))
 .then((results) => { console.log(`Results from first test: ${results}`) });

function test(arg) {
 return new Promise((resolve, reject) => {
  resolve(arg + 1);
 });
}
So what's happening here? I am passing the number zero to the function test. It is taking that number, adding one to it, and returning a new Promise. .then((results) => test(results) then takes that result and passes it to the test function again which again adds one and returns a new Promise. Finally, we are printing the result which is 3. Now here is a different way to write that code with an implicit argument:
test(0)
 .then(test)
 .then(test)
 .then(results => { console.log(`Results from second test: ${results}`) });

function test(arg) {
 return new Promise((resolve, reject) => {
  resolve(arg + 1);
 });
}

The result is the same. In this case, .then(test) is the equivalent of .then((results) => test(results)). This shorthand notation only works for a single argument. If you have multiple arguments to your function, you will have to write out the full code.

Tuesday, April 7, 2020

MySQL Subqueries

For most of my software development career, I used an Oracle database. At my current job, we use a MySQL database. So I am still learning the ins and outs of MySQL. Here is a common thing I would do in Oracle:
SELECT
  name, total_points
FROM (
  SELECT
    name,
    SUM(points) AS total_points
  FROM
    scores
  GROUP BY
    name
)
WHERE
  total_points >  10
ORDER BY
  total_points DESC, name
That would give me a list of all players who have scored more than 10 points. When I tried this same format in MySQL, I got this error:
Error Code: 1248. Every derived table must have its own alias
After a little searching, I came up with the solution. I had to name my inner query. So with a slight modification it worked:
SELECT
  tp.name, tp.total_points
FROM (
  SELECT
    name,
    SUM(points) AS total_points
  FROM
    scores
  GROUP BY
    name
) tp
WHERE
  total_points >  10
ORDER BY
  total_points DESC, name


Monday, April 6, 2020

Accessibility and Social Media Management

There is a local radio program that I used to listen to that gives advice on yards and gardening. The host is very knowledgeable and is my go-to resource when I have questions about planting, fertilizing, and weeding. Here was an exchange this morning between (presumably) the social media manager for his account and one of his followers regarding a video that was posted:


The follower asked a simple question but got back a seemingly harsh response. First, let's look at why the follower may have misunderstood the audio.
  • · Maybe the follower is hard of hearing
  • · Maybe the follower's native language in not English
  • · Maybe the follower was in a noisy environment
  • · Maybe all of the above are true
The response from the author did not take any of these things into account. That is why accessibility is important not just for us web developers but for content providers as well. A better response might have been:

Thank you for your question. He was saying "rose soil". Here is an example of a product that we recommend.

Here is another thing to consider. Various sites such as this one report that 85% of Facebook videos are watched without sound! Additionally the World Health Organization reports that globally over 400 million people suffering from some sort of hearing loss. So if you are relying solely on audio on your site to get your message across, you are alienating a large group of people. This is why it is so important to provide captioning on your social media videos.

I realize that not everyone will have the skills, times, and/or technology to caption their videos. So in that case, a good fallback plan when someone poses a questions about the audio is to just be kind.

Tuesday, February 18, 2020

Git Status on Command Line Does Not Match Git Status in VSCode.

A couple of times recently I ran into a situation where VSCode was showing a lot of untracked files in git but the command line was not. Today when I opened up VSCode, it showed over 60 untracked items while the command line only showed 15. It turned out to be a simple misunderstanding on my part.

To demonstrate, I created a new git project with a new subdirectory called new-dir. Inside of new-dir, I created 3 files.

When I run git status on the command line without any parameters, it collapses new directories into single entries.

Command line example

By examining the git output in VSCode, I see now that it is running the command git status -z -u. The -z just terminates each entry in the list with a NUL instead of LF presumably for better parsing. The -u lists out each untracked file individually. And this makes sense because in the source control window of VSCode, you want to be able to see the diff of each file individually.

View of source control panel in VSCode

 
Blogger Templates