I have an XBMC box hooked up to my TV. In addition, I have 2 Foscam IP cameras, which I use for baby-monitoring my two little girls.
A while back I thought it'd be cool to write a couple of scripts, so that when one of the Foscam's alarms triggers, I get a notification on XBMC along with live images from the relevant Foscam camera. With this, I could watch TV, while keeping a close eye on the kiddos.
So, on the XBMC box I have a shell script running, which checks the Foscam alarm statuses every second. If an alarm is triggered, it sends a command to XBMC to fire an XBMC script, suspends its checks for 30 seconds, and resumes checking the alarm statuses. The XBMC script shows a 30-second notification, containing the name of one of my daughters (depending on which camera triggered) and a cute picture of them, and a live snapshot from the relevant Foscam, updated every half a second.
This all worked perfectly fine, and was totally awesome :) However, last week I upgraded the Foscam's firmware. The only change in the new firmware (mentioned in the firmware description) was to change the camera's HTTP
authorization method from basic
to digest
. And ever since then, I have been having problems with my XBMC script.
So first, here is the current version of the script:
# Import the XBMC/XBMCGUI modules.
from requests.auth import HTTPDigestAuth
import xbmc, xbmcgui, xbmcvfs, xbmcaddon
import sys, os, requests, time
# Class to manage the notification image
class CamView(xbmcgui.WindowDialog):
urlpath = "/snapshot.cgi?resolution=16"
imagename = "snapshot.jpg"
def __init__(self, camname,camport,idx,username,password):
# Construct correct URL
self.baseurl = 'http://' + camname + 'cam:' + camport
# Cams use digest authentication
self.auth = HTTPDigestAuth(username, password)
# Set
path = xbmc.translatePath('special://profile/addon_data/%s' % xbmcaddon.Addon().getAddonInfo('id'))
if not xbmcvfs.exists(path):
xbmcvfs.mkdir(path)
self.imagefile = os.path.join(path, self.imagename)
# Message
self.msg = {
"1": camname.capitalize() + ' moved',
"3": camname.capitalize() + ' made a sound',
}.get(idx, camname.capitalize() + 'cam fired alarm')
# set the initial image before the window is shown
self.image = xbmcgui.ControlImage(870, 383, 380, 253, "")
self.addControl(self.image)
def update_image(self):
f = requests.get(self.baseurl+self.urlpath, auth=self.auth)
with open(self.imagefile, "wb") as local_file:
local_file.write(f.content)
self.image.setImage("")
self.image.setImage(self.imagefile)
def __enter__(self):
return self
def __exit__(self,type,value,traceback):
os.remove(self.imagefile)
def main():
for i in range(1,len(sys.argv)):
str,dummy,val = sys.argv[i].partition("=")
if str == "alarm_id": idx = val
if str == "cam_id" : camname = val
if str == "cam_port": camport = val
if str == "username": username = val
if str == "password": password = val
with CamView(camname,camport,idx,username,password) as viewer:
viewer.show()
start_time = time.time()
firstimage = True
while(time.time() - start_time <= 30):
viewer.update_image()
curr_time = round(time.time()-start_time, 0)
if firstimage:
firstimage = False
nowtime = time.strftime("%I:%M %p")
viewer.image.setAnimations([('conditional', 'effect=fade start=0 end=100 time=750 delay=125 condition=true'),
('conditional', 'effect=slide start=400,0 end=0,0 time=750 condition=true')])
xoptions = ("Notification(\"" + viewer.msg + "\", " + nowtime + ", 29500, special://masterprofile/addon_data/" +
xbmcaddon.Addon().getAddonInfo('id') + "/" + camname + ".png)")
xbmc.executebuiltin(xoptions)
elif curr_time == 30:
viewer.image.setAnimations([('conditional', 'effect=fade start=100 end=0 time=750 condition=true'),
('conditional', 'effect=slide start=0,0 end=400,0 time=750 condition=true')])
else:
viewer.image.setAnimations([('conditional', 'effect=fade start=100 end=100 time=0 condition=true')])
xbmc.sleep(500)
if __name__ == "__main__":
if xbmc.getInfoLabel("Window(10000).Property(foscamScriptRunning)") == "True":
xbmc.log('Script already running', level=xbmc.LOGERROR)
else:
xbmc.log('Foscam alarm triggered', level=xbmc.LOGNOTICE)
xbmcgui.Window(10000).setProperty("foscamScriptRunning", "True")
main()
xbmcgui.Window(10000).setProperty("foscamScriptRunning", "False")
The original script used urllib
, which I found did not support digest
authentication in any sort of convenient way. So I changed to urllib2
. This did not work, in that the image in my XBMC popup did not ever update after the first image. Sometimes, there was not even an image at all.
So I did a bit of digging, and I quickly found that getting the snapshots with urllib2
with Digest
authentication took a little over 7 seconds! (with the old firmware, this took a little under 0.1 seconds). Thinking that this might be the cause of the images not being updated, I changed everything to the requests
module. Profiling showed that obtaining a single snapshot from the camera now took somewhere around 0.25 seconds; still rather slow IMHO, but perhaps acceptable. However, also with this method, the notification images are not updating.
I'm triggering the script via remote SSH, so I can check the XBMC logs etc. I also checked the time stamps on the snapshot.jpg
files as they were created, and they seem to agree with the script trigger time and the 0.25 second delay by requests
. In the XBMC script, I changed the order of clearing the image and setting it to the new snapshot to every possible ordering you can think of, without success. If I put a delay between clearing and re-setting the image, I see a flickering image, suggesting that it all works. However, it is always re-set to the exact same snapshot.
So, I'm really quite stuck. What am I overlooking here?