The morning hustle features everyone in the house going every which way. It must be quite entertaining for our pets watching us! In our particular swirl of AM chaos, there is one persistent question for the last person to leave the house, "Has the fish been fed?"
Recently heard, two colleagues talking at lunch:
"But someone has to tell it the fish were fed, right?"
"True, but no matter what, a person has to do something to indicate the fish was fed. Put up a note card, leave the food out. There has to be a signal with some extra effort"
"You could have a sensor to detect when the top is opened."
"True, but that doesn't mean the fish is fed. Just that the top was flipped. It would have to know the difference between opening and feeding . . .any sensor solution gets complicated quickly."
"What about an automatic feeder?"
"You would still need a way to confirm that the food was delivered. A webcam? Food weight sensors? Computer vision? What about a fail-safe? You see it all gets complicated very quickly!"
One of the more popular Maker IoT projects is the fish feeder type of project. No doubt about it, this is a crowded space, with some very sophisticated solutions: Bounce a laser off the moon from anywhere on earth, pellets fall, fish fed, twitter and your social network notified, and all of it SHA256 hashed into a blockchain!
However, back in the real world, I just need to know if the fish was fed!
We've tried leaving notes, leaving the measuring spoon out, etc. but all these methods have their drawbacks and I have been informed that we are not hanging a whiteboard up anywhere. Most of these solutions require some kind of extra effort, and have a degree of ambiguity to them. Most importantly they don't fit into the morning flow!
So what do we do? In the end the simplest solution to date is if there is any doubt, the last person to leave the house feeds the fish. The fish loves this, but overfeeding is no good for the fish or the tank!
The inherent nature of the voice assistant provides the lowest effort solution! Just tell Mycroft if you fed the fish:
"Hey Mycroft, I fed the fish"
Behind the scenes, the utterance is detected and logged with a date and time stamp. If any one wants to know if the fish was fed today, they just ask:
"Hey Mycroft, has the fish been fed today?"
Behind the scenes Mycroft accesses the feeding log, compares the date of the latest entry with the current date. People are good at talking, computers are good at calculating and keeping track of things!
No fuss, no muss, no 6 month, multi-thousand dollar magic mirror project, web servers, IoT not needed, just a few hours of programming and testing! It fits into the flow of the morning. You feed the fish and expend little effort telling Mycroft you did it!The voice assistant technology, embodied in Mycroft, provides the most natural, least effort solution!
Mycroft skills are written in Python and so if you can program it in Python, you can make a skill with Mycroft that will do it.
Handling dates and times in python is made easy using libraries such as time, datetime and pytz. It can be a real pain dealing with time zones, etc. These libraries and the many tutorials, blog posts and stackoverflow posts make most problems a cut and paste issue.
When you set you set up your Mycroft, your location information is stored in a configuration file in json format. You can access the information in this file from within your skill:
from mycroft.configuration.config import Configuration ... class FedTheFishSkill(MycroftSkill): # The constructor of the skill, which calls MycroftSkill's constructor def __init__(self): super(FedTheFishSkill, self).__init__(name="FedTheFishSkill") # Initialize working variables used within the skill. self.myTimeZone = (Configuration.get())["location"]["timezone"]["code"]
Take note, that the settings you implement for the Mark 1, do not change the system time on the Raspberry Pi running your Mark 1.
You can check this with by running the following:
# https://howchoo.com/g/ywi5m2vkodk/working-with-datetime-objects-and-timezones-i$ # http://www.pythonforbeginners.com/basics/python-datetime-time-examples/ import time import pytz import datetime from mycroft.configuration.config import Configuration config = Configuration.get() myTimeZone = (Configuration.get())["location"]["timezone"]["code"] utc_now = pytz.utc.localize(datetime.datetime.utcnow()) est_now = utc_now.astimezone(pytz.timezone(myTimeZone)) print myTimeZone print "System time: ", utc_now print "My Local time: ", est_now
Produces the following output:
... America/New_York System time: 2018-03-10 03:37:52.931258+00:00 My Local time: 2018-03-09 22:37:52.931258-05:00
When we notify Mycroft we had feed the fish, the data needs to be stored so that it can be accessed later. We need to keep a log of feeding. In order to account for power outages and to be able check back or "audit" our feeding, I decided it was best to save the information in a file.
File IO is python is easy. Simply open a file, and write to it:
with open(self.settings["log_location"] + "mostRecent.log", "w") as logFile: logFile.write(timeString) logFile.close()
We use our settings.json file to customize the location of our log files and keep the directory structure of the skill organized. In my case, this corresponds to: /opt/mycroft/skills/skill-fed-the-fish/logs/.
You can change this to whatever you want, just make sure that Mycroft has permission to work with that folder:
The filename is "mostRecent.log" and it is intended to contain one and only entry that is the time and date stamp of the last recorded feeding of the fish. This is ensured with the used of the "w" parameter:
w opens a file for writing only. Overwrites the file if the file exists. If the file does not exist, creates a new file for writing()
The file contents look like this:
Friday March 09 12 02 AM
The skill has two intents and I use the adapt intent parser to determine what intent to use from the detected utterance.
"Hey Mycroft, I fed the fish" and "Hey Mycroft, has the fish been fed today?"
I specify this in the skill with:
@intent_handler(IntentBuilder("").require("Fed").require("Fish")) def handle_fed_fish_intent(self, message):
@intent_handler(IntentBuilder("").require("Has").require("Fish").require("Fed")) def handle_was_fed(self, message):
When someone tells Mycroft they have fed the fish, he logs this in 2 files. One, as above, is the most recent time and date stamp of the feeding. Another is a log of all feedings. This acts as an audit trial of feedings and could be used later for other functionality.
When someone ask Mycroft if the fish has been fed, Mycroft looks at the mostRecent.log file and read this line into a string. I then split this string into a list of blank space elements and grab the month and day of the most recent feeding.
We then compare the current month and day to the logged month and day of last feeding, if they are the same, the fish has been fed, if not the fish has not been fed. I strip leading zero from the day if present to eventually use for a later intent that will let ask the specifics about the feeding. This will let Mycroft respond with something like, "eight thirty" and "not zero eight thirty".
I think grabbing the month is a bit of programmer overkill. This is to avoid a conflict between say, March 8th and April 8th. But in the real world, if the fish hasn't been fed for a month, you and the fish have bigger problems . . .
if (int(dayFed) == int(time_now.strftime("%d"))) and (monthFed == time_now.strftime("%B")): self.speak("yes the fish was fed today") else: self.speak("no the fish was not fed today")
The complete code is in my github repo. and linked to below.
The following demo shows how skill works after midnight. It's a new day and now Mycroft will tell you the fish has not been fed. Once we indicate the fish has been fed, we are good again . . .
Should we have Mycroft send an e-mail if the fish has been fed? Do I need yet another e-mail in my inbox? I think when asked, Mycroft, should tell me when exactly the fish was fed. Instead of worrying about the month, maybe some date arithmetic to avoid double feeding at midnight? But this again seems like programmer overkill. I definitely need to limit the audit log to perhaps 30 days of entires at the most . . .Let me know if yo have any ideas!
So on the first morning of testing , the skill worked! The fish looked as if he hadn't been fed and lo and behold when i asked, the answer from Mycroft was no! SO I fed the fish. Later, when everyone was home, I asked my son if he had fed the fish and the answer was "no" So we are 1 for 1!
And that't it, so far problem solved. Sometimes very useful things, are very simple! Now about that cat . . .