diff --git a/README.md b/README.md
index 9243c29f4f993667ba2601a6d35185868ec6c272..addc012804460db4828954c5cc689b01aee5d029 100644
--- a/README.md
+++ b/README.md
@@ -34,47 +34,54 @@ A screenshot of the debug interface (battery and charging states are controlled
 
 To interface with the API requests should include the parameter api_password which matches the api_password in home-assistant.conf.
 
-The following API commands are currently supported:
-
-    /api/state/categories - POST
-    parameter: api_password - string
-    Will list all the categories for which a state is currently tracked. Returns a json object like this:
-
-    ```json
-    {"status": "OK", 
-     "message":"State categories", 
-     "categories": ["all_devices", "Paulus_Nexus_4"]}
-    ```
-
-    /api/state/get - POST
-    parameter: api_password - string
-    parameter: category - string
-    Will get the current state of a category. Returns a json object like this:
-
-    ```json
-    {"status": "OK", 
-     "message": "State of all_devices",
-     "category": "all_devices",
-     "state": "device_home",
-     "last_changed": "19:10:39 25-10-2013",
-     "attributes": {}}
-    ```
-
-    /api/state/change - POST
-    parameter: api_password - string
-    parameter: category - string
-    parameter: new_state - string
-    parameter: attributes - object encoded as JSON string (optional)
-    Changes category 'category' to 'new_state'
-    It is possible to sent multiple values for category and new_state.
-    If the number of values for category and new_state do not match only
-    combinations where both values are supplied will be set.
-    
-    /api/event/fire - POST
-    parameter: api_password - string
-    parameter: event_name - string
-    parameter: event_data - object encoded as JSON string (optional)
-    Fires an 'event_name' event containing data from 'event_data'
+All API calls have to be accompanied by an 'api_password' parameter and will
+return JSON. If successful calls will return status code 200 or 201.
+
+Other status codes that can occur are:
+ - 400 (Bad Request)
+ - 401 (Unauthorized)
+ - 404 (Not Found)
+ - 405 (Method not allowed)
+
+The api supports the following actions:
+
+`/api/states` - GET
+Returns a list of categories for which a state is available
+Example result:
+```json{
+    "categories": [
+        "Paulus_Nexus_4", 
+        "weather.sun", 
+        "all_devices"
+    ]
+}```
+
+`/api/states/<category>` - GET
+Returns the current state from a category
+Example result:
+```json{
+    "attributes": {
+        "next_rising": "07:04:15 29-10-2013", 
+        "next_setting": "18:00:31 29-10-2013"
+    }, 
+    "category": "weather.sun", 
+    "last_changed": "23:24:33 28-10-2013", 
+    "state": "below_horizon"
+}```
+
+`/api/states/<category>` - POST
+Updates the current state of a category. Returns status code 201 if successful
+with location header of updated resource.
+parameter: new_state - string
+optional parameter: attributes - JSON encoded object
+
+`/api/events/<event_type>` - POST
+Fires an event with event_type
+optional parameter: event_data - JSON encoded object
+Example result:
+```json{
+    "message": "Event download_file fired."
+}```
 
 Android remote control
 ----------------------
diff --git a/android-tasker/Home_Assistant.apk b/android-tasker/Home_Assistant.apk
index 9f75e86569e92be7575400784529d87cd3fb408f..9e3924aa8f2fc42f16aad271b0675be0de965d1d 100644
Binary files a/android-tasker/Home_Assistant.apk and b/android-tasker/Home_Assistant.apk differ
diff --git a/android-tasker/Home_Assistant.prj.xml b/android-tasker/Home_Assistant.prj.xml
index 4b789d07dd9afdc8ec3300dbe28814cf41b47505..5a2ff8e8a575fe2c7acdc308a32212c9cf364fad 100644
--- a/android-tasker/Home_Assistant.prj.xml
+++ b/android-tasker/Home_Assistant.prj.xml
@@ -1,7 +1,8 @@
 <TaskerData sr="" dvi="1" tv="4.1u3m">
 	<Profile sr="prof24" ve="2">
 		<cdate>1381116787665</cdate>
-		<edate>1381116787665</edate>
+		<clp>true</clp>
+		<edate>1382062270688</edate>
 		<id>24</id>
 		<mid0>20</mid0>
 		<Event sr="con0" ve="2">
@@ -11,8 +12,7 @@
 	</Profile>
 	<Profile sr="prof25" ve="2">
 		<cdate>1380613730755</cdate>
-		<clp>true</clp>
-		<edate>1381001553706</edate>
+		<edate>1382769497429</edate>
 		<id>25</id>
 		<mid0>23</mid0>
 		<mid1>20</mid1>
@@ -26,7 +26,7 @@
 	<Profile sr="prof26" ve="2">
 		<cdate>1380613730755</cdate>
 		<clp>true</clp>
-		<edate>1381110280839</edate>
+		<edate>1383003483161</edate>
 		<id>26</id>
 		<mid0>22</mid0>
 		<mid1>20</mid1>
@@ -37,13 +37,27 @@
 			<Int sr="arg0" val="3"/>
 		</State>
 	</Profile>
+	<Profile sr="prof3" ve="2">
+		<cdate>1380613730755</cdate>
+		<clp>true</clp>
+		<edate>1383003498566</edate>
+		<id>3</id>
+		<mid0>10</mid0>
+		<mid1>20</mid1>
+		<nme>HA Power AC</nme>
+		<pri>10</pri>
+		<State sr="con0">
+			<code>10</code>
+			<Int sr="arg0" val="1"/>
+		</State>
+	</Profile>
 	<Profile sr="prof5" ve="2">
 		<cdate>1380496514959</cdate>
 		<cldm>1500</cldm>
 		<clp>true</clp>
-		<edate>1381110261999</edate>
+		<edate>1382769618501</edate>
 		<id>5</id>
-		<mid0>7</mid0>
+		<mid0>19</mid0>
 		<nme>HA Battery Changed</nme>
 		<Event sr="con0" ve="2">
 			<code>203</code>
@@ -53,14 +67,14 @@
 	<Project sr="proj0">
 		<cdate>1381110247781</cdate>
 		<name>Home Assistant</name>
-		<pids>24,26,5,25</pids>
+		<pids>5,3,25,26,24</pids>
 		<scenes>Variable Query,Home Assistant Start</scenes>
-		<tids>14,16,4,15,7,20,6,8,22,23,9,11,12,13</tids>
+		<tids>19,8,10,6,16,9,20,14,11,4,23,15,12,13,22</tids>
 		<Kid sr="Kid">
 			<launchID>12</launchID>
 			<pkg>nl.paulus.homeassistant</pkg>
-			<vnme>1.0</vnme>
-			<vnum>10</vnum>
+			<vnme>1.1</vnme>
+			<vnum>14</vnum>
 		</Kid>
 		<Img sr="icon" ve="2">
 			<nme>cust_animal_penguin</nme>
@@ -69,7 +83,7 @@
 	<Scene sr="sceneHome Assistant Start">
 		<backColour>-637534208</backColour>
 		<cdate>1381113309678</cdate>
-		<edate>1381118413367</edate>
+		<edate>1381162068611</edate>
 		<heightLand>-1</heightLand>
 		<heightPort>688</heightPort>
 		<nme>Home Assistant Start</nme>
@@ -308,9 +322,24 @@
 			<Int sr="arg2" val="255"/>
 		</ImageElement>
 	</Scene>
+	<Task sr="task10">
+		<cdate>1380613530339</cdate>
+		<edate>1383030846230</edate>
+		<id>10</id>
+		<nme>Charging AC</nme>
+		<Action sr="act0" ve="3">
+			<code>130</code>
+			<Str sr="arg0" ve="3">Update Charging</Str>
+			<Int sr="arg1" val="0"/>
+			<Int sr="arg2" val="5"/>
+			<Str sr="arg3" ve="3">ac</Str>
+			<Str sr="arg4" ve="3"/>
+			<Str sr="arg5" ve="3"/>
+		</Action>
+	</Task>
 	<Task sr="task11">
 		<cdate>1381110672417</cdate>
-		<edate>1381116046765</edate>
+		<edate>1383030844501</edate>
 		<id>11</id>
 		<nme>Open Debug Interface</nme>
 		<pri>10</pri>
@@ -321,7 +350,7 @@
 	</Task>
 	<Task sr="task12">
 		<cdate>1381113015963</cdate>
-		<edate>1381116866174</edate>
+		<edate>1383030888271</edate>
 		<id>12</id>
 		<nme>Start Screen</nme>
 		<pri>10</pri>
@@ -338,6 +367,9 @@
 			<code>49</code>
 			<Str sr="arg0" ve="3">Home Assistant Start</Str>
 		</Action>
+		<Img sr="icn" ve="2">
+			<nme>hd_aaa_ext_tiles_small</nme>
+		</Img>
 	</Task>
 	<Task sr="task13">
 		<cdate>1381114398467</cdate>
@@ -354,16 +386,15 @@
 	</Task>
 	<Task sr="task14">
 		<cdate>1381114829583</cdate>
-		<edate>1381115098684</edate>
+		<edate>1383030731979</edate>
 		<id>14</id>
 		<nme>API Fire Event</nme>
 		<pri>10</pri>
 		<Action sr="act0" ve="3">
 			<code>116</code>
 			<Str sr="arg0" ve="3">%HA_HOST:%HA_PORT</Str>
-			<Str sr="arg1" ve="3">/api/event/fire</Str>
-			<Str sr="arg2" ve="3">api_password=%HA_API_PASSWORD
-event_name=%par1</Str>
+			<Str sr="arg1" ve="3">/api/events/%par1</Str>
+			<Str sr="arg2" ve="3">api_password=%HA_API_PASSWORD</Str>
 			<Str sr="arg3" ve="3"/>
 			<Int sr="arg4" val="10"/>
 			<Str sr="arg5" ve="3"/>
@@ -372,7 +403,7 @@ event_name=%par1</Str>
 	</Task>
 	<Task sr="task15">
 		<cdate>1380262442154</cdate>
-		<edate>1381115642332</edate>
+		<edate>1383030894445</edate>
 		<id>15</id>
 		<nme>Light On</nme>
 		<pri>10</pri>
@@ -391,7 +422,7 @@ event_name=%par1</Str>
 	</Task>
 	<Task sr="task16">
 		<cdate>1380262442154</cdate>
-		<edate>1381115613658</edate>
+		<edate>1383030896170</edate>
 		<id>16</id>
 		<nme>Start Epic Sax</nme>
 		<pri>10</pri>
@@ -408,9 +439,29 @@ event_name=%par1</Str>
 			<nme>hd_aaa_ext_guitar</nme>
 		</Img>
 	</Task>
+	<Task sr="task19">
+		<cdate>1380262442154</cdate>
+		<edate>1383030903842</edate>
+		<id>19</id>
+		<nme>Update Battery</nme>
+		<pri>10</pri>
+		<Action sr="act0" ve="3">
+			<code>116</code>
+			<Str sr="arg0" ve="3">%HA_HOST:%HA_PORT</Str>
+			<Str sr="arg1" ve="3">/api/state/change</Str>
+			<Str sr="arg2" ve="3">api_password=%HA_API_PASSWORD
+category=%HA_DEVICE_NAME.charging
+new_state=%HA_CHARGING
+attributes={"battery":%BATT}</Str>
+			<Str sr="arg3" ve="3"/>
+			<Int sr="arg4" val="10"/>
+			<Str sr="arg5" ve="3"/>
+			<Str sr="arg6" ve="3"/>
+		</Action>
+	</Task>
 	<Task sr="task20">
 		<cdate>1380613530339</cdate>
-		<edate>1381116102459</edate>
+		<edate>1383030848142</edate>
 		<id>20</id>
 		<nme>Charging None</nme>
 		<Action sr="act0" ve="3">
@@ -425,7 +476,7 @@ event_name=%par1</Str>
 	</Task>
 	<Task sr="task22">
 		<cdate>1380613530339</cdate>
-		<edate>1381116000403</edate>
+		<edate>1383030909347</edate>
 		<id>22</id>
 		<nme>Charging Wireless</nme>
 		<Action sr="act0" ve="3">
@@ -440,7 +491,7 @@ event_name=%par1</Str>
 	</Task>
 	<Task sr="task23">
 		<cdate>1380613530339</cdate>
-		<edate>1381115997137</edate>
+		<edate>1383030849758</edate>
 		<id>23</id>
 		<nme>Charging USB</nme>
 		<Action sr="act0" ve="3">
@@ -455,7 +506,7 @@ event_name=%par1</Str>
 	</Task>
 	<Task sr="task4">
 		<cdate>1380262442154</cdate>
-		<edate>1381115633261</edate>
+		<edate>1383030892718</edate>
 		<id>4</id>
 		<nme>Light Off</nme>
 		<pri>10</pri>
@@ -474,7 +525,7 @@ event_name=%par1</Str>
 	</Task>
 	<Task sr="task6">
 		<cdate>1380522560890</cdate>
-		<edate>1381117976853</edate>
+		<edate>1383030900554</edate>
 		<id>6</id>
 		<nme>Setup</nme>
 		<pri>10</pri>
@@ -580,28 +631,9 @@ event_name=%par1</Str>
 			<nme>hd_ab_action_settings</nme>
 		</Img>
 	</Task>
-	<Task sr="task7">
-		<cdate>1380262442154</cdate>
-		<edate>1381111978825</edate>
-		<id>7</id>
-		<nme>Update Battery</nme>
-		<pri>10</pri>
-		<Action sr="act0" ve="3">
-			<code>116</code>
-			<Str sr="arg0" ve="3">%HA_HOST:%HA_PORT</Str>
-			<Str sr="arg1" ve="3">/api/state/change</Str>
-			<Str sr="arg2" ve="3">api_password=%HA_API_PASSWORD
-category=%HA_DEVICE_NAME.battery
-new_state=%BATT</Str>
-			<Str sr="arg3" ve="3"/>
-			<Int sr="arg4" val="10"/>
-			<Str sr="arg5" ve="3"/>
-			<Str sr="arg6" ve="3"/>
-		</Action>
-	</Task>
 	<Task sr="task8">
 		<cdate>1380262442154</cdate>
-		<edate>1381115955507</edate>
+		<edate>1383030906782</edate>
 		<id>8</id>
 		<nme>Update Charging</nme>
 		<pri>10</pri>
@@ -613,23 +645,18 @@ new_state=%BATT</Str>
 			<Int sr="arg3" val="0"/>
 		</Action>
 		<Action sr="act1" ve="3">
-			<code>116</code>
-			<Str sr="arg0" ve="3">%HA_HOST:%HA_PORT</Str>
-			<Str sr="arg1" ve="3">/api/state/change</Str>
-			<Str sr="arg2" ve="3">api_password=%HA_API_PASSWORD
-category=%HA_DEVICE_NAME.charging
-new_state=%HA_CHARGING
-category=%HA_DEVICE_NAME.battery
-new_state=%BATT</Str>
+			<code>130</code>
+			<Str sr="arg0" ve="3">Update Battery</Str>
+			<Int sr="arg1" val="0"/>
+			<Int sr="arg2" val="5"/>
 			<Str sr="arg3" ve="3"/>
-			<Int sr="arg4" val="10"/>
+			<Str sr="arg4" ve="3"/>
 			<Str sr="arg5" ve="3"/>
-			<Str sr="arg6" ve="3"/>
 		</Action>
 	</Task>
 	<Task sr="task9">
 		<cdate>1380262442154</cdate>
-		<edate>1381115659673</edate>
+		<edate>1383030890674</edate>
 		<id>9</id>
 		<nme>Start Fireplace</nme>
 		<pri>10</pri>
diff --git a/docs/screenshot-debug-interface.png b/docs/screenshot-debug-interface.png
index 35b0dd86ff831efd8573c59bf24634b7033a24f3..631e080c877bd20fc12a2cdfa56d0950173f673b 100644
Binary files a/docs/screenshot-debug-interface.png and b/docs/screenshot-debug-interface.png differ
diff --git a/homeassistant/httpinterface.py b/homeassistant/httpinterface.py
index 68a1e3086b0cac9af1d396015198377d4c916504..b2563c00dd282d17e00ab9df60ba21ae77009fac 100644
--- a/homeassistant/httpinterface.py
+++ b/homeassistant/httpinterface.py
@@ -4,31 +4,63 @@ homeassistant.httpinterface
 
 This module provides an API and a HTTP interface for debug purposes.
 
-By default it will run on port 8080.
+By default it will run on port 8123.
 
-All API calls have to be accompanied by an 'api_password' parameter.
+All API calls have to be accompanied by an 'api_password' parameter and will
+return JSON. If successful calls will return status code 200 or 201.
+
+Other status codes that can occur are:
+ - 400 (Bad Request)
+ - 401 (Unauthorized)
+ - 404 (Not Found)
+ - 405 (Method not allowed)
 
 The api supports the following actions:
 
-/api/state/change - POST
-parameter: category - string
+/api/states - GET
+Returns a list of categories for which a state is available
+Example result:
+{
+    "categories": [
+        "Paulus_Nexus_4",
+        "weather.sun",
+        "all_devices"
+    ]
+}
+
+/api/states/<category> - GET
+Returns the current state from a category
+Example result:
+{
+    "attributes": {
+        "next_rising": "07:04:15 29-10-2013",
+        "next_setting": "18:00:31 29-10-2013"
+    },
+    "category": "weather.sun",
+    "last_changed": "23:24:33 28-10-2013",
+    "state": "below_horizon"
+}
+
+/api/states/<category> - POST
+Updates the current state of a category. Returns status code 201 if successful
+with location header of updated resource.
 parameter: new_state - string
-Changes category 'category' to 'new_state'
-It is possible to sent multiple values for category and new_state.
-If the number of values for category and new_state do not match only
-combinations where both values are supplied will be set.
+optional parameter: attributes - JSON encoded object
 
-/api/event/fire - POST
-parameter: event_name - string
-parameter: event_data - JSON-string (optional)
-Fires an 'event_name' event containing data from 'event_data'
+/api/events/<event_type> - POST
+Fires an event with event_type
+optional parameter: event_data - JSON encoded object
+Example result:
+{
+    "message": "Event download_file fired."
+}
 
 """
 
 import json
 import threading
-import itertools
 import logging
+import re
 from BaseHTTPServer import BaseHTTPRequestHandler, HTTPServer
 from urlparse import urlparse, parse_qs
 
@@ -36,9 +68,24 @@ import homeassistant as ha
 
 SERVER_PORT = 8123
 
-MESSAGE_STATUS_OK = "OK"
-MESSAGE_STATUS_ERROR = "ERROR"
-MESSAGE_STATUS_UNAUTHORIZED = "UNAUTHORIZED"
+HTTP_OK = 200
+HTTP_CREATED = 201
+HTTP_MOVED_PERMANENTLY = 301
+HTTP_BAD_REQUEST = 400
+HTTP_UNAUTHORIZED = 401
+HTTP_NOT_FOUND = 404
+HTTP_METHOD_NOT_ALLOWED = 405
+
+URL_ROOT = "/"
+
+URL_STATES_CATEGORY = "/states/{}"
+URL_API_STATES = "/api/states"
+URL_API_STATES_CATEGORY = "/api/states/{}"
+
+URL_EVENTS_EVENT = "/events/{}"
+URL_API_EVENTS = "/api/events"
+URL_API_EVENTS_EVENT = "/api/events/{}"
+
 
 class HTTPInterface(threading.Thread):
     """ Provides an HTTP interface for Home Assistant. """
@@ -76,277 +123,278 @@ class HTTPInterface(threading.Thread):
 class RequestHandler(BaseHTTPRequestHandler):
     """ Handles incoming HTTP requests """
 
-    #Handler for the GET requests
-    def do_GET(self):    # pylint: disable=invalid-name
-        """ Handle incoming GET requests. """
-        write = lambda txt: self.wfile.write(txt+"\n")
+    PATHS = [ ('GET', '/', '_handle_get_root'),
 
-        url = urlparse(self.path)
+              # /states
+              ('GET', '/states', '_handle_get_states'),
+              ('GET', re.compile(r'/states/(?P<category>[a-zA-Z\.\_0-9]+)'),
+                                    '_handle_get_states_category'),
+              ('POST', re.compile(r'/states/(?P<category>[a-zA-Z\.\_0-9]+)'),
+                                    '_handle_post_states_category'),
 
-        get_data = parse_qs(url.query)
+              # /events
+              ('POST', re.compile(r'/events/(?P<event_type>\w+)'),
+                                    '_handle_post_events_event_type')
+                ]
 
-        api_password = get_data.get('api_password', [''])[0]
-
-        if url.path == "/":
-            if self._verify_api_password(api_password, False):
-                self.send_response(200)
-                self.send_header('Content-type','text/html')
-                self.end_headers()
-
-
-                write(("<html>"
-                       "<head><title>Home Assistant</title></head>"
-                       "<body>"))
+    def _handle_request(self, method): # pylint: disable=too-many-branches
+        """ Does some common checks and calls appropriate method. """
+        url = urlparse(self.path)
 
-                # Flash message support
-                if self.server.flash_message:
-                    write("<h3>{}</h3>".format(self.server.flash_message))
+        # Read query input
+        data = parse_qs(url.query)
 
-                    self.server.flash_message = None
+        # Did we get post input ?
+        content_length = int(self.headers.get('Content-Length', 0))
 
-                # Describe state machine:
-                categories = []
+        if content_length:
+            data.update(parse_qs(self.rfile.read(content_length)))
 
-                write(("<table><tr>"
-                       "<th>Name</th><th>State</th>"
-                       "<th>Last Changed</th><th>Attributes</th></tr>"))
+        try:
+            api_password = data['api_password'][0]
+        except KeyError:
+            api_password = ''
 
-                for category in \
-                    sorted(self.server.statemachine.categories,
-                                            key=lambda key: key.lower()):
+        # We respond to API requests with JSON
+        # For other requests we respond with html
+        if url.path.startswith('/api/'):
+            path = url.path[4:]
+            # pylint: disable=attribute-defined-outside-init
+            self.use_json = True
 
-                    categories.append(category)
+        else:
+            path = url.path
+            # pylint: disable=attribute-defined-outside-init
+            self.use_json = False
 
-                    state = self.server.statemachine.get_state(category)
 
-                    attributes = "<br>".join(
-                        ["{}: {}".format(attr, state['attributes'][attr])
-                         for attr in state['attributes']])
+        path_matched_but_not_method = False
+        handle_request_method = False
 
-                    write(("<tr>"
-                           "<td>{}</td><td>{}</td><td>{}</td><td>{}</td>"
-                           "</tr>").
-                        format(category,
-                               state['state'],
-                               state['last_changed'],
-                               attributes))
+        # Check every url to find matching result
+        for t_method, t_path, t_handler in RequestHandler.PATHS:
 
-                write("</table>")
+            # we either do string-comparison or regular expression matching
+            if isinstance(t_path, str):
+                path_match = path == t_path
+            else:
+                path_match = t_path.match(path) #pylint:disable=maybe-no-member
 
-                # Small form to change the state
-                write(("<br />Change state:<br />"
-                       "<form action='state/change' method='POST'>"))
 
-                write("<input type='hidden' name='api_password' value='{}' />".
-                        format(self.server.api_password))
+            if path_match and method == t_method:
+                # Call the method
+                handle_request_method = getattr(self, t_handler)
+                break
 
-                write("<select name='category'>")
+            elif path_match:
+                path_matched_but_not_method = True
 
-                for category in categories:
-                    write("<option>{}</option>".format(category))
 
-                write("</select>")
+        if handle_request_method:
 
-                write(("<input name='new_state' />"
-                       "<input type='submit' value='set state' />"
-                       "</form>"))
+            if self._verify_api_password(api_password):
+                handle_request_method(path_match, data)
 
-                # Describe event bus:
-                write(("<table><tr><th>Event</th><th>Listeners</th></tr>"))
+        elif path_matched_but_not_method:
+            self.send_response(HTTP_METHOD_NOT_ALLOWED)
 
-                for category in sorted(self.server.eventbus.listeners,
-                                                key=lambda key: key.lower()):
-                    write("<tr><td>{}</td><td>{}</td></tr>".
-                        format(category,
-                               len(self.server.eventbus.listeners[category])))
+        else:
+            self.send_response(HTTP_NOT_FOUND)
 
-                # Form to allow firing events
-                write(("</table><br />"
-                       "<form action='event/fire' method='POST'>"))
 
-                write("<input type='hidden' name='api_password' value='{}' />".
-                        format(self.server.api_password))
+    def do_GET(self): # pylint: disable=invalid-name
+        """ GET request handler. """
+        self._handle_request('GET')
 
-                write(("Event name: <input name='event_name' /><br />"
-                       "Event data (json): <input name='event_data' /><br />"
-                       "<input type='submit' value='fire event' />"
-                       "</form>"))
+    def do_POST(self): # pylint: disable=invalid-name
+        """ POST request handler. """
+        self._handle_request('POST')
 
-                write("</body></html>")
+    def _verify_api_password(self, api_password):
+        """ Helper method to verify the API password
+            and take action if incorrect. """
+        if api_password == self.server.api_password:
+            return True
 
+        elif self.use_json:
+            self._message("API password missing or incorrect.",
+                                                            HTTP_UNAUTHORIZED)
 
         else:
-            self.send_response(404)
+            self.send_response(HTTP_OK)
+            self.send_header('Content-type','text/html')
+            self.end_headers()
+
+            self.wfile.write((
+                   "<html>"
+                   "<head><title>Home Assistant</title></head>"
+                   "<body>"
+                   "<form action='/' method='GET'>"
+                   "API password: <input name='api_password' />"
+                   "<input type='submit' value='submit' />"
+                   "</form>"
+                   "</body></html>"))
 
-    # pylint: disable=invalid-name, too-many-branches, too-many-statements
-    def do_POST(self):
-        """ Handle incoming POST requests. """
+        return False
 
-        length = int(self.headers['Content-Length'])
-        post_data = parse_qs(self.rfile.read(length))
+    # pylint: disable=unused-argument
+    def _handle_get_root(self, path_match, data):
+        """ Renders the debug interface. """
 
-        if self.path.startswith('/api/'):
-            action = self.path[5:]
-            use_json = True
+        write = lambda txt: self.wfile.write(txt+"\n")
 
-        else:
-            action = self.path[1:]
-            use_json = False
+        self.send_response(HTTP_OK)
+        self.send_header('Content-type','text/html')
+        self.end_headers()
 
-        given_api_password = post_data.get("api_password", [''])[0]
+        write(("<html>"
+               "<head><title>Home Assistant</title></head>"
+               "<body>"))
 
-        # Action to change the state
-        if action == "state/categories":
-            if self._verify_api_password(given_api_password, use_json):
-                self._response(use_json, "State categories",
-                    json_data=
-                        {'categories': self.server.statemachine.categories})
+        # Flash message support
+        if self.server.flash_message:
+            write("<h3>{}</h3>".format(self.server.flash_message))
 
-        elif action == "state/get":
-            if self._verify_api_password(given_api_password, use_json):
-                try:
-                    category = post_data['category'][0]
+            self.server.flash_message = None
 
-                    state = self.server.statemachine.get_state(category)
+        # Describe state machine:
+        categories = []
 
-                    state['category'] = category
+        write(("<table><tr>"
+               "<th>Name</th><th>State</th>"
+               "<th>Last Changed</th><th>Attributes</th></tr>"))
 
-                    self._response(use_json, "State of {}".format(category),
-                                   json_data=state)
+        for category in \
+            sorted(self.server.statemachine.categories,
+                                    key=lambda key: key.lower()):
 
+            categories.append(category)
 
-                except KeyError:
-                    # If category or new_state don't exist in post data
-                    self._response(use_json, "Invalid state received.",
-                                                        MESSAGE_STATUS_ERROR)
+            state = self.server.statemachine.get_state(category)
 
-        elif action == "state/change":
-            if self._verify_api_password(given_api_password, use_json):
-                try:
-                    changed = []
+            attributes = "<br>".join(
+                ["{}: {}".format(attr, state['attributes'][attr])
+                 for attr in state['attributes']])
 
-                    for idx, category, new_state in zip(itertools.count(),
-                                                        post_data['category'],
-                                                        post_data['new_state']
-                                                       ):
+            write(("<tr>"
+                   "<td>{}</td><td>{}</td><td>{}</td><td>{}</td>"
+                   "</tr>").
+                format(category,
+                       state['state'],
+                       state['last_changed'],
+                       attributes))
 
-                        # See if we also received attributes for this state
-                        try:
-                            attributes = json.loads(
-                                                post_data['attributes'][idx])
-                        except KeyError:
-                            # Happens if key 'attributes' or idx does not exist
-                            attributes = None
+        write("</table>")
 
-                        self.server.statemachine.set_state(category,
-                                                           new_state,
-                                                           attributes)
+        # Describe event bus:
+        write(("<table><tr><th>Event</th><th>Listeners</th></tr>"))
 
-                        changed.append("{}={}".format(category, new_state))
+        for category in sorted(self.server.eventbus.listeners,
+                                        key=lambda key: key.lower()):
+            write("<tr><td>{}</td><td>{}</td></tr>".
+                format(category,
+                       len(self.server.eventbus.listeners[category])))
 
-                    self._response(use_json, "States changed: {}".
-                                                format( ", ".join(changed) ) )
+        # Form to allow firing events
+        write("</table>")
 
-                except KeyError:
-                    # If category or new_state don't exist in post data
-                    self._response(use_json, "Invalid parameters received.",
-                                                        MESSAGE_STATUS_ERROR)
+        write("</body></html>")
 
-                except ValueError:
-                    # If json.loads doesn't understand the attributes
-                    self._response(use_json, "Invalid state data received.",
-                                                        MESSAGE_STATUS_ERROR)
+    # pylint: disable=unused-argument
+    def _handle_get_states(self, path_match, data):
+        """ Returns the categories which state is being tracked. """
+        self._write_json({'categories': self.server.statemachine.categories})
 
-        # Action to fire an event
-        elif action == "event/fire":
-            if self._verify_api_password(given_api_password, use_json):
-                try:
-                    event_name = post_data['event_name'][0]
+    # pylint: disable=unused-argument
+    def _handle_get_states_category(self, path_match, data):
+        """ Returns the state of a specific category. """
+        try:
+            category = path_match.group('category')
 
-                    if (not 'event_data' in post_data or
-                        post_data['event_data'][0] == ""):
+            state = self.server.statemachine.get_state(category)
 
-                        event_data = None
+            state['category'] = category
 
-                    else:
-                        event_data = json.loads(post_data['event_data'][0])
+            self._write_json(state)
 
-                    self.server.eventbus.fire(event_name, event_data)
+        except KeyError:
+            # If category or new_state don't exist in post data
+            self._message("Invalid state received.", HTTP_BAD_REQUEST)
 
-                    self._response(use_json, "Event {} fired.".
-                                                format(event_name))
 
-                except ValueError:
-                    # If JSON decode error
-                    self._response(use_json, "Invalid event received (1).",
-                                                        MESSAGE_STATUS_ERROR)
+    def _handle_post_states_category(self, path_match, data):
+        """ Handles updating the state of a category. """
+        try:
+            category = path_match.group('category')
 
-                except KeyError:
-                    # If "event_name" not in post_data
-                    self._response(use_json, "Invalid event received (2).",
-                                                        MESSAGE_STATUS_ERROR)
+            new_state = data['new_state'][0]
 
-        else:
-            self.send_response(404)
+            try:
+                attributes = json.loads(data['attributes'][0])
+            except KeyError:
+                # Happens if key 'attributes' does not exist
+                attributes = None
 
+            self.server.statemachine.set_state(category,
+                                               new_state,
+                                               attributes)
 
-    def _verify_api_password(self, api_password, use_json):
-        """ Helper method to verify the API password
-            and take action if incorrect. """
-        if api_password == self.server.api_password:
-            return True
+            self._redirect("/states/{}".format(category),
+                           "State changed: {}={}".format(category, new_state),
+                           HTTP_CREATED)
 
-        elif use_json:
-            self._response(True, "API password missing or incorrect.",
-                                                MESSAGE_STATUS_UNAUTHORIZED)
+        except KeyError:
+            # If category or new_state don't exist in post data
+            self._message("Invalid parameters received.",
+                                                HTTP_BAD_REQUEST)
 
-        else:
-            self.send_response(200)
-            self.send_header('Content-type','text/html')
-            self.end_headers()
+        except ValueError:
+            # Occurs during error parsing json
+            self._message("Invalid JSON for attributes", HTTP_BAD_REQUEST)
 
-            write = lambda txt: self.wfile.write(txt+"\n")
+    def _handle_post_events_event_type(self, path_match, data):
+        """ Handles firing of an event. """
+        event_type = path_match.group('event_type')
 
-            write(("<html>"
-                   "<head><title>Home Assistant</title></head>"
-                   "<body>"
-                   "<form action='/' method='GET'>"
-                   "API password: <input name='api_password' />"
-                   "<input type='submit' value='submit' />"
-                   "</form>"
-                   "</body></html>"))
+        try:
+            try:
+                event_data = json.loads(data['event_data'][0])
+            except KeyError:
+                # Happens if key 'event_data' does not exist
+                event_data = None
 
-        return False
+            self.server.eventbus.fire(event_type, event_data)
 
-    def _response(self, use_json, message,
-                        status=MESSAGE_STATUS_OK, json_data=None):
-        """ Helper method to show a message to the user. """
-        log_message = "{}: {}".format(status, message)
+            self._message("Event {} fired.".format(event_type))
 
-        if status == MESSAGE_STATUS_OK:
-            self.server.logger.info(log_message)
-            response_code = 200
+        except ValueError:
+            # Occurs during error parsing json
+            self._message("Invalid JSON for event_data", HTTP_BAD_REQUEST)
 
+    def _message(self, message, status_code=HTTP_OK):
+        """ Helper method to return a message to the caller. """
+        if self.use_json:
+            self._write_json({'message': message}, status_code=status_code)
         else:
-            self.server.logger.error(log_message)
-            response_code = (401 if status == MESSAGE_STATUS_UNAUTHORIZED
-                                                                    else 400)
+            self._redirect('/', message)
 
-        if use_json:
-            self.send_response(response_code)
-            self.send_header('Content-type','application/json')
-            self.end_headers()
-
-            json_data = json_data or {}
-            json_data['status'] = status
-            json_data['message'] = message
+    def _redirect(self, location, message=None,
+                  status_code=HTTP_MOVED_PERMANENTLY):
+        """ Helper method to redirect caller. """
+        # Only save as flash message if we will go to debug interface next
+        if not self.use_json and message:
+            self.server.flash_message = message
 
-            self.wfile.write(json.dumps(json_data))
+        self.send_response(status_code)
+        self.send_header("Location", "{}?api_password={}".
+                            format(location, self.server.api_password))
+        self.end_headers()
 
-        else:
-            self.server.flash_message = message
+    def _write_json(self, data=None, status_code=HTTP_OK):
+        """ Helper method to return JSON to the caller. """
+        self.send_response(status_code)
+        self.send_header('Content-type','application/json')
+        self.end_headers()
 
-            self.send_response(301)
-            self.send_header("Location", "/?api_password={}".
-                                format(self.server.api_password))
-            self.end_headers()
+        if data:
+            self.wfile.write(json.dumps(data, indent=4, sort_keys=True))
diff --git a/homeassistant/remote.py b/homeassistant/remote.py
index 93ab356d1317762d6633b988b8bac0eb383867f5..8e59f31c69a777c46a7a0f7932ef570cd845bdec 100644
--- a/homeassistant/remote.py
+++ b/homeassistant/remote.py
@@ -12,25 +12,33 @@ HomeAssistantException will be raised.
 import threading
 import logging
 import json
+import urlparse
 
 import requests
 
 import homeassistant as ha
-import homeassistant.httpinterface as httpinterface
+import homeassistant.httpinterface as hah
 
-def _setup_call_api(host, port, base_path, api_password):
+METHOD_GET = "get"
+METHOD_POST = "post"
+
+def _setup_call_api(host, port, api_password):
     """ Helper method to setup a call api method. """
-    port = port or httpinterface.SERVER_PORT
+    port = port or hah.SERVER_PORT
 
-    base_url = "http://{}:{}/api/{}".format(host, port, base_path)
+    base_url = "http://{}:{}".format(host, port)
 
-    def _call_api(action, data=None):
+    def _call_api(method, path, data=None):
         """ Makes a call to the Home Assistant api. """
         data = data or {}
-
         data['api_password'] = api_password
 
-        return requests.post(base_url + action, data=data)
+        url = urlparse.urljoin(base_url, path)
+
+        if method == METHOD_GET:
+            return requests.get(url, params=data)
+        else:
+            return requests.request(method, url, data=data)
 
     return _call_api
 
@@ -43,21 +51,19 @@ class EventBus(ha.EventBus):
     def __init__(self, host, api_password, port=None):
         ha.EventBus.__init__(self)
 
-        self._call_api = _setup_call_api(host, port, "event/", api_password)
+        self._call_api = _setup_call_api(host, port, api_password)
 
         self.logger = logging.getLogger(__name__)
 
     def fire(self, event_type, event_data=None):
         """ Fire an event. """
 
-        if not event_data:
-            event_data = {}
-
-        data = {'event_name': event_type,
-                'event_data': json.dumps(event_data)}
+        data = {'event_data': json.dumps(event_data)} if event_data else None
 
         try:
-            req = self._call_api("fire", data)
+            req = self._call_api(METHOD_POST,
+                                 hah.URL_API_EVENTS_EVENT.format(event_type),
+                                 data)
 
             if req.status_code != 200:
                 error = "Error firing event: {} - {}".format(
@@ -66,7 +72,6 @@ class EventBus(ha.EventBus):
                 self.logger.error("EventBus:{}".format(error))
                 raise ha.HomeAssistantException(error)
 
-
         except requests.exceptions.ConnectionError:
             self.logger.exception("EventBus:Error connecting to server")
 
@@ -91,7 +96,7 @@ class StateMachine(ha.StateMachine):
     def __init__(self, host, api_password, port=None):
         ha.StateMachine.__init__(self, None)
 
-        self._call_api = _setup_call_api(host, port, "state/", api_password)
+        self._call_api = _setup_call_api(host, port, api_password)
 
         self.lock = threading.Lock()
         self.logger = logging.getLogger(__name__)
@@ -101,7 +106,7 @@ class StateMachine(ha.StateMachine):
         """ List of categories which states are being tracked. """
 
         try:
-            req = self._call_api("categories")
+            req = self._call_api(METHOD_GET, hah.URL_API_STATES)
 
             return req.json()['categories']
 
@@ -126,14 +131,15 @@ class StateMachine(ha.StateMachine):
 
         self.lock.acquire()
 
-        data = {'category': category,
-                'new_state': new_state,
+        data = {'new_state': new_state,
                 'attributes': json.dumps(attributes)}
 
         try:
-            req = self._call_api('change', data)
+            req = self._call_api(METHOD_POST,
+                                 hah.URL_API_STATES_CATEGORY.format(category),
+                                 data)
 
-            if req.status_code != 200:
+            if req.status_code != 201:
                 error = "Error changing state: {} - {}".format(
                             req.status_code, req.text)
 
@@ -152,7 +158,8 @@ class StateMachine(ha.StateMachine):
             the state of the specified category. """
 
         try:
-            req = self._call_api("get", {'category': category})
+            req = self._call_api(METHOD_GET,
+                                 hah.URL_API_STATES_CATEGORY.format(category))
 
             data = req.json()
 
diff --git a/homeassistant/test.py b/homeassistant/test.py
index 1c45e1114fd9e1d905f0d880bbea6e517b628838..0ca1650b5335baca1922785ba24e5fe11bdf31f6 100644
--- a/homeassistant/test.py
+++ b/homeassistant/test.py
@@ -13,13 +13,13 @@ import requests
 
 import homeassistant as ha
 import homeassistant.remote as remote
-import homeassistant.httpinterface as httpinterface
+import homeassistant.httpinterface as hah
 
 
 
 API_PASSWORD = "test1234"
 
-HTTP_BASE_URL = "http://127.0.0.1:{}".format(httpinterface.SERVER_PORT)
+HTTP_BASE_URL = "http://127.0.0.1:{}".format(hah.SERVER_PORT)
 
 # pylint: disable=too-many-public-methods
 class TestHTTPInterface(unittest.TestCase):
@@ -27,13 +27,16 @@ class TestHTTPInterface(unittest.TestCase):
 
     HTTP_init = False
 
+    def _url(self, path=""):
+        """ Helper method to generate urls. """
+        return HTTP_BASE_URL + path
+
     def setUp(self):    # pylint: disable=invalid-name
         """ Initialize the HTTP interface if not started yet. """
         if not TestHTTPInterface.HTTP_init:
             TestHTTPInterface.HTTP_init = True
 
-            httpinterface.HTTPInterface(self.eventbus, self.statemachine,
-                                                                API_PASSWORD)
+            hah.HTTPInterface(self.eventbus, self.statemachine, API_PASSWORD)
 
             self.statemachine.set_state("test", "INIT_STATE")
             self.sm_with_remote_eb.set_state("test", "INIT_STATE")
@@ -55,17 +58,21 @@ class TestHTTPInterface(unittest.TestCase):
     def test_debug_interface(self):
         """ Test if we can login by comparing not logged in screen to
             logged in screen. """
-        self.assertNotEqual(requests.get(HTTP_BASE_URL).text,
-                            requests.get("{}/?api_password={}".format(
-                                HTTP_BASE_URL, API_PASSWORD)).text)
+
+        with_pw = requests.get(
+                    self._url("/?api_password={}".format(API_PASSWORD)))
+
+        without_pw = requests.get(self._url())
+
+        self.assertNotEqual(without_pw.text, with_pw.text)
 
 
     def test_debug_state_change(self):
         """ Test if the debug interface allows us to change a state. """
-        requests.post("{}/state/change".format(HTTP_BASE_URL),
-            data={"category":"test",
-                  "new_state":"debug_state_change",
-                  "api_password":API_PASSWORD})
+        requests.post(
+            self._url(hah.URL_STATES_CATEGORY.format("test")),
+                        data={"new_state":"debug_state_change",
+                              "api_password":API_PASSWORD})
 
         self.assertEqual(self.statemachine.get_state("test")['state'],
                          "debug_state_change")
@@ -74,19 +81,21 @@ class TestHTTPInterface(unittest.TestCase):
     def test_api_password(self):
         """ Test if we get access denied if we omit or provide
             a wrong api password. """
-        req = requests.post("{}/api/state/change".format(HTTP_BASE_URL))
+        req = requests.post(
+                self._url(hah.URL_API_STATES_CATEGORY.format("test")))
 
         self.assertEqual(req.status_code, 401)
 
-        req = requests.post("{}/api/state/change".format(HTTP_BASE_URL,
-                data={"api_password":"not the password"}))
+        req = requests.post(
+                self._url(hah.URL_API_STATES_CATEGORY.format("test")),
+                data={"api_password":"not the password"})
 
         self.assertEqual(req.status_code, 401)
 
 
     def test_api_list_state_categories(self):
         """ Test if the debug interface allows us to list state categories. """
-        req = requests.post("{}/api/state/categories".format(HTTP_BASE_URL),
+        req = requests.get(self._url(hah.URL_API_STATES),
                 data={"api_password":API_PASSWORD})
 
         data = req.json()
@@ -96,16 +105,15 @@ class TestHTTPInterface(unittest.TestCase):
 
 
     def test_api_get_state(self):
-        """ Test if the debug interface allows us to list state categories. """
-        req = requests.post("{}/api/state/get".format(HTTP_BASE_URL),
-                data={"api_password":API_PASSWORD,
-                      "category": "test"})
+        """ Test if the debug interface allows us to get a state. """
+        req = requests.get(
+                self._url(hah.URL_API_STATES_CATEGORY.format("test")),
+                data={"api_password":API_PASSWORD})
 
         data = req.json()
 
         state = self.statemachine.get_state("test")
 
-
         self.assertEqual(data['category'], "test")
         self.assertEqual(data['state'], state['state'])
         self.assertEqual(data['last_changed'], state['last_changed'])
@@ -117,9 +125,8 @@ class TestHTTPInterface(unittest.TestCase):
 
         self.statemachine.set_state("test", "not_to_be_set_state")
 
-        requests.post("{}/api/state/change".format(HTTP_BASE_URL),
-            data={"category":"test",
-                  "new_state":"debug_state_change2",
+        requests.post(self._url(hah.URL_API_STATES_CATEGORY.format("test")),
+            data={"new_state":"debug_state_change2",
                   "api_password":API_PASSWORD})
 
         self.assertEqual(self.statemachine.get_state("test")['state'],
@@ -156,22 +163,6 @@ class TestHTTPInterface(unittest.TestCase):
         self.assertEqual(state['attributes']['test'], 1)
 
 
-    def test_api_multiple_state_change(self):
-        """ Test if we can change multiple states in 1 request. """
-
-        self.statemachine.set_state("test", "not_to_be_set_state")
-        self.statemachine.set_state("test2", "not_to_be_set_state")
-
-        requests.post("{}/api/state/change".format(HTTP_BASE_URL),
-            data={"category": ["test", "test2"],
-                  "new_state": ["test_state_1", "test_state_2"],
-                  "api_password":API_PASSWORD})
-
-        self.assertEqual(self.statemachine.get_state("test")['state'],
-                         "test_state_1")
-        self.assertEqual(self.statemachine.get_state("test2")['state'],
-                         "test_state_2")
-
     # pylint: disable=invalid-name
     def test_api_state_change_of_non_existing_category(self):
         """ Test if the API allows us to change a state of
@@ -179,15 +170,16 @@ class TestHTTPInterface(unittest.TestCase):
 
         new_state = "debug_state_change"
 
-        req = requests.post("{}/api/state/change".format(HTTP_BASE_URL),
-                data={"category":"test_category_that_does_not_exist",
-                      "new_state":new_state,
-                      "api_password":API_PASSWORD})
+        req = requests.post(
+                self._url(hah.URL_API_STATES_CATEGORY.format(
+                                        "test_category_that_does_not_exist")),
+                data={"new_state": new_state,
+                      "api_password": API_PASSWORD})
 
         cur_state = (self.statemachine.
-                        get_state("test_category_that_does_not_exist")['state'])
+                       get_state("test_category_that_does_not_exist")['state'])
 
-        self.assertEqual(req.status_code, 200)
+        self.assertEqual(req.status_code, 201)
         self.assertEqual(cur_state, new_state)
 
     # pylint: disable=invalid-name
@@ -201,10 +193,9 @@ class TestHTTPInterface(unittest.TestCase):
 
         self.eventbus.listen_once("test_event_no_data", listener)
 
-        requests.post("{}/api/event/fire".format(HTTP_BASE_URL),
-            data={"event_name":"test_event_no_data",
-                  "event_data":"",
-                  "api_password":API_PASSWORD})
+        requests.post(
+            self._url(hah.URL_EVENTS_EVENT.format("test_event_no_data")),
+            data={"api_password":API_PASSWORD})
 
         # Allow the event to take place
         time.sleep(1)
@@ -224,9 +215,9 @@ class TestHTTPInterface(unittest.TestCase):
 
         self.eventbus.listen_once("test_event_with_data", listener)
 
-        requests.post("{}/api/event/fire".format(HTTP_BASE_URL),
-            data={"event_name":"test_event_with_data",
-                  "event_data":'{"test": 1}',
+        requests.post(
+            self._url(hah.URL_EVENTS_EVENT.format("test_event_with_data")),
+            data={"event_data":'{"test": 1}',
                   "api_password":API_PASSWORD})
 
         # Allow the event to take place
@@ -235,28 +226,6 @@ class TestHTTPInterface(unittest.TestCase):
         self.assertEqual(len(test_value), 1)
 
 
-    # pylint: disable=invalid-name
-    def test_api_fire_event_with_no_params(self):
-        """ Test how the API respsonds when we specify no event attributes. """
-        test_value = []
-
-        def listener(event):
-            """ Helper method that will verify that our event got called and
-                that test if our data came through. """
-            if "test" in event.data:
-                test_value.append(1)
-
-        self.eventbus.listen_once("test_event_with_data", listener)
-
-        requests.post("{}/api/event/fire".format(HTTP_BASE_URL),
-            data={"api_password":API_PASSWORD})
-
-        # Allow the event to take place
-        time.sleep(1)
-
-        self.assertEqual(len(test_value), 0)
-
-
     # pylint: disable=invalid-name
     def test_api_fire_event_with_invalid_json(self):
         """ Test if the API allows us to fire an event. """
@@ -268,9 +237,9 @@ class TestHTTPInterface(unittest.TestCase):
 
         self.eventbus.listen_once("test_event_with_bad_data", listener)
 
-        req = requests.post("{}/api/event/fire".format(HTTP_BASE_URL),
-            data={"event_name":"test_event_with_bad_data",
-                  "event_data":'not json',
+        req = requests.post(
+            self._url(hah.URL_API_EVENTS_EVENT.format("test_event")),
+            data={"event_data":'not json',
                   "api_password":API_PASSWORD})