Repository: fronzbot/blinkpy

Issue:
Ignore case when accessing camera name
Currently the camera dictionary can be accessed with the camera name, but case matters.  We should ignore case sensitivity here to make it less annoying to access.

Discussion:

Patch:
diff --git a/CHANGES.rst b/CHANGES.rst
index 3452982..be1cf91 100644
--- a/CHANGES.rst
+++ b/CHANGES.rst
@@ -6,6 +6,7 @@ A list of changes between each release
 0.7.0.dev (development version)
 ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
 - Fixed style errors for bumped pydocstring and pylint versions
+- Changed Blink.cameras dictionary to be case-insensitive (fixes `#35 <https://github.com/fronzbot/blinkpy/issues/35>`_)
 
 0.6.0 (2017-05-12)
 ^^^^^^^^^^^^^^^^^^
diff --git a/README.rst b/README.rst
index 2e02f9b..d3eb6f5 100644
--- a/README.rst
+++ b/README.rst
@@ -1,7 +1,6 @@
 blinkpy |Build Status| |Coverage Status|
 =============================================
 A Python library for the Blink Camera system
-Only compatible with Python 3+
 
 Disclaimer:
 ~~~~~~~~~~~~~~~
@@ -51,8 +50,8 @@ The cameras are of a BlinkCamera class, of which the following parameters can be
     blink = blinkpy.Blink(username='YOUR USER NAME', password='YOUR PASSWORD')
     blink.setup_system()
     
-    for name, camera in blink.cameras.items():
-        print(name)                  # Name of the camera
+    for camera in blink.cameras:
+        print(camera.name)           # Name of the camera
         print(camera.id)             # Integer id of the camera (assigned by Blink)
         print(camera.armed)          # Whether the device is armed/disarmed (ie. detecting motion)
         print(camera.clip)           # Link to last motion clip captured
@@ -62,7 +61,7 @@ The cameras are of a BlinkCamera class, of which the following parameters can be
         print(camera.battery_string) # Gives battery level as a string ("OK" or "Low").  Returns "Unknown" if value is... well, unknown 
         print(camera.notifications)  # Number of unread notifications (ie. motion alerts that haven't been viewed)
         print(camera.motion)         # Dictionary containing values for keys ['video', 'image', 'time']
-                                     #   which corresponds to last motion recorded, thumbnail of last motion, and timestamp of last motion
+                                     # which correspond to last motion recorded, thumbnail of last motion, and timestamp of last motion
 
 
 Class Descriptions
diff --git a/blinkpy.py b/blinkpy.py
index 12cdd67..2ce09da 100644
--- a/blinkpy.py
+++ b/blinkpy.py
@@ -16,6 +16,7 @@ import json
 import getpass
 from shutil import copyfileobj
 import requests
+from requests.structures import CaseInsensitiveDict
 import helpers.errors as ERROR
 from helpers.constants import (BLINK_URL, LOGIN_URL,
                                LOGIN_BACKUP_URL,
@@ -186,7 +187,7 @@ class Blink(object):
         self.region_id = None
         self._host = None
         self._events = []
-        self.cameras = {}
+        self.cameras = CaseInsensitiveDict({})
         self._idlookup = {}
         self.urls = None
 
diff --git a/helpers/__init__.py b/helpers/__init__.py
index 0a53750..e74901c 100644
--- a/helpers/__init__.py
+++ b/helpers/__init__.py
@@ -1,3 +1,3 @@
 """Init file for blinkpy helper functions."""
 from helpers import constants
-from helpers import errors
\ No newline at end of file
+from helpers import errors


Test patch:
diff --git a/tests/test_blink_functions.py b/tests/test_blink_functions.py
index cb2ba8b..be4c49f 100644
--- a/tests/test_blink_functions.py
+++ b/tests/test_blink_functions.py
@@ -2,6 +2,7 @@
 
 import unittest
 from unittest import mock
+import random
 import blinkpy
 import tests.mock_responses as mresp
 
@@ -120,6 +121,26 @@ class TestBlinkFunctions(unittest.TestCase):
             camera.urls.home_url = "use_bad_response"
             self.assertEqual(camera.image_refresh(), None)
 
+    @mock.patch('blinkpy.blinkpy.requests.post',
+                side_effect=mresp.mocked_requests_post)
+    @mock.patch('blinkpy.blinkpy.requests.get',
+                side_effect=mresp.mocked_requests_get)
+    def test_camera_random_case(self, mock_get, mock_post):
+        """Checks for case of camera name."""
+        self.blink.setup_system()
+        for camera_name in self.blink.cameras:
+
+            rand_name = camera_name
+            # Make sure we never pass this test if rand_name = camera_name
+            while rand_name == camera_name:
+                rand_name = ''.join(
+                    random.choice(
+                        (str.upper, str.lower)
+                    )(x) for x in camera_name)
+
+            self.assertEqual(self.blink.cameras[camera_name].name,
+                             self.blink.cameras[rand_name].name)
+
     def test_camera_update(self):
         """Checks that the update function is doing the right thing."""
         self.test_urls = blinkpy.BlinkURLHandler('test')