diff --git a/.coveragerc b/.coveragerc
index caadb341b596da01eae0dbdb4fdaea5b191b2fbf..e9856c7a51e940f9123588c19597821341743daa 100644
--- a/.coveragerc
+++ b/.coveragerc
@@ -108,9 +108,11 @@ omit =
     homeassistant/components/*/zoneminder.py
 
     homeassistant/components/alarm_control_panel/alarmdotcom.py
+    homeassistant/components/alarm_control_panel/concord232.py
     homeassistant/components/alarm_control_panel/nx584.py
     homeassistant/components/alarm_control_panel/simplisafe.py
     homeassistant/components/binary_sensor/arest.py
+    homeassistant/components/binary_sensor/concord232.py
     homeassistant/components/binary_sensor/rest.py
     homeassistant/components/browser.py
     homeassistant/components/camera/bloomsky.py
diff --git a/homeassistant/components/alarm_control_panel/concord232.py b/homeassistant/components/alarm_control_panel/concord232.py
new file mode 100755
index 0000000000000000000000000000000000000000..0e0fd026b60dbcdf7f1c40f4513b5b0f20ca3488
--- /dev/null
+++ b/homeassistant/components/alarm_control_panel/concord232.py
@@ -0,0 +1,136 @@
+"""
+Support for Concord232 alarm control panels.
+
+For more details about this platform, please refer to the documentation at
+https://home-assistant.io/components/alarm_control_panel.concord232/
+"""
+
+import datetime
+
+import logging
+
+import homeassistant.components.alarm_control_panel as alarm
+from homeassistant.components.alarm_control_panel import PLATFORM_SCHEMA
+from homeassistant.const import (
+    CONF_HOST, CONF_NAME, CONF_PORT,
+    STATE_ALARM_ARMED_AWAY, STATE_ALARM_ARMED_HOME,
+    STATE_ALARM_DISARMED, STATE_UNKNOWN)
+import homeassistant.helpers.config_validation as cv
+
+import requests
+
+import voluptuous as vol
+
+REQUIREMENTS = ['concord232==0.14']
+
+_LOGGER = logging.getLogger(__name__)
+
+DEFAULT_HOST = 'localhost'
+DEFAULT_NAME = 'CONCORD232'
+DEFAULT_PORT = 5007
+
+PLATFORM_SCHEMA = PLATFORM_SCHEMA.extend({
+    vol.Optional(CONF_PORT, default=DEFAULT_PORT): cv.port,
+    vol.Optional(CONF_NAME, default=DEFAULT_NAME): cv.string,
+    vol.Optional(CONF_HOST, default=DEFAULT_HOST): cv.string,
+})
+
+SCAN_INTERVAL = 1
+
+
+def setup_platform(hass, config, add_devices, discovery_info=None):
+    """Setup concord232 platform."""
+    name = config.get(CONF_NAME)
+    host = config.get(CONF_HOST)
+    port = config.get(CONF_PORT)
+
+    url = 'http://{}:{}'.format(host, port)
+
+    try:
+        add_devices([Concord232Alarm(hass, url, name)])
+    except requests.exceptions.ConnectionError as ex:
+        _LOGGER.error('Unable to connect to Concord232: %s', str(ex))
+        return False
+
+
+class Concord232Alarm(alarm.AlarmControlPanel):
+    """Represents the Concord232-based alarm panel."""
+
+    def __init__(self, hass, url, name):
+        """Initalize the concord232 alarm panel."""
+        from concord232 import client as concord232_client
+
+        self._state = STATE_UNKNOWN
+        self._hass = hass
+        self._name = name
+        self._url = url
+
+        try:
+            client = concord232_client.Client(self._url)
+        except requests.exceptions.ConnectionError as ex:
+            _LOGGER.error('Unable to connect to Concord232: %s', str(ex))
+
+        self._alarm = client
+        self._alarm.partitions = self._alarm.list_partitions()
+        self._alarm.last_partition_update = datetime.datetime.now()
+        self.update()
+
+    @property
+    def should_poll(self):
+        """Polling needed."""
+        return True
+
+    @property
+    def name(self):
+        """Return the name of the device."""
+        return self._name
+
+    @property
+    def code_format(self):
+        """The characters if code is defined."""
+        return '[0-9]{4}([0-9]{2})?'
+
+    @property
+    def state(self):
+        """Return the state of the device."""
+        return self._state
+
+    def update(self):
+        """Update values from API."""
+        try:
+            part = self._alarm.list_partitions()[0]
+        except requests.exceptions.ConnectionError as ex:
+            _LOGGER.error('Unable to connect to %(host)s: %(reason)s',
+                          dict(host=self._url, reason=ex))
+            newstate = STATE_UNKNOWN
+        except IndexError:
+            _LOGGER.error('concord232 reports no partitions')
+            newstate = STATE_UNKNOWN
+
+        if part['arming_level'] == "Off":
+            newstate = STATE_ALARM_DISARMED
+        elif "Home" in part['arming_level']:
+            newstate = STATE_ALARM_ARMED_HOME
+        else:
+            newstate = STATE_ALARM_ARMED_AWAY
+
+        if not newstate == self._state:
+            _LOGGER.info("State Chnage from %s to %s", self._state, newstate)
+            self._state = newstate
+        return self._state
+
+    def alarm_disarm(self, code=None):
+        """Send disarm command."""
+        self._alarm.disarm(code)
+
+    def alarm_arm_home(self, code=None):
+        """Send arm home command."""
+        self._alarm.arm('home')
+
+    def alarm_arm_away(self, code=None):
+        """Send arm away command."""
+        self._alarm.arm('auto')
+
+    def alarm_trigger(self, code=None):
+        """Alarm trigger command."""
+        raise NotImplementedError()
diff --git a/homeassistant/components/binary_sensor/concord232.py b/homeassistant/components/binary_sensor/concord232.py
new file mode 100755
index 0000000000000000000000000000000000000000..bc1eab4694a104c0119ecb6d01c18e0a412fdc89
--- /dev/null
+++ b/homeassistant/components/binary_sensor/concord232.py
@@ -0,0 +1,143 @@
+"""
+Support for exposing Concord232 elements as sensors.
+
+For more details about this platform, please refer to the documentation at
+https://home-assistant.io/components/binary_sensor.concord232/
+"""
+import datetime
+
+import logging
+
+from homeassistant.components.binary_sensor import (
+    BinarySensorDevice, PLATFORM_SCHEMA, SENSOR_CLASSES)
+from homeassistant.const import (CONF_HOST, CONF_PORT)
+
+import homeassistant.helpers.config_validation as cv
+
+import requests
+
+import voluptuous as vol
+
+
+REQUIREMENTS = ['concord232==0.14']
+
+_LOGGER = logging.getLogger(__name__)
+
+CONF_EXCLUDE_ZONES = 'exclude_zones'
+CONF_ZONE_TYPES = 'zone_types'
+
+DEFAULT_HOST = 'localhost'
+DEFAULT_PORT = '5007'
+DEFAULT_SSL = False
+
+ZONE_TYPES_SCHEMA = vol.Schema({
+    cv.positive_int: vol.In(SENSOR_CLASSES),
+})
+
+PLATFORM_SCHEMA = PLATFORM_SCHEMA.extend({
+    vol.Optional(CONF_EXCLUDE_ZONES, default=[]):
+        vol.All(cv.ensure_list, [cv.positive_int]),
+    vol.Optional(CONF_HOST, default=DEFAULT_HOST): cv.string,
+    vol.Optional(CONF_PORT, default=DEFAULT_PORT): cv.port,
+    vol.Optional(CONF_ZONE_TYPES, default={}): ZONE_TYPES_SCHEMA,
+})
+
+SCAN_INTERVAL = 1
+
+DEFAULT_NAME = "Alarm"
+
+
+# pylint: disable=too-many-locals
+def setup_platform(hass, config, add_devices, discovery_info=None):
+    """Setup the Concord232 binary sensor platform."""
+    from concord232 import client as concord232_client
+
+    host = config.get(CONF_HOST)
+    port = config.get(CONF_PORT)
+    exclude = config.get(CONF_EXCLUDE_ZONES)
+    zone_types = config.get(CONF_ZONE_TYPES)
+    sensors = []
+
+    try:
+        _LOGGER.debug('Initializing Client.')
+        client = concord232_client.Client('http://{}:{}'
+                                          .format(host, port))
+        client.zones = client.list_zones()
+        client.last_zone_update = datetime.datetime.now()
+
+    except requests.exceptions.ConnectionError as ex:
+        _LOGGER.error('Unable to connect to Concord232: %s', str(ex))
+        return False
+
+    for zone in client.zones:
+        _LOGGER.info('Loading Zone found: %s', zone['name'])
+        if zone['number'] not in exclude:
+            sensors.append(Concord232ZoneSensor(
+                hass,
+                client,
+                zone,
+                zone_types.get(zone['number'], get_opening_type(zone))))
+
+        add_devices(sensors)
+
+    return True
+
+
+def get_opening_type(zone):
+    """Helper function to try to guess sensor type frm name."""
+    if "MOTION" in zone["name"]:
+        return "motion"
+    if "KEY" in zone["name"]:
+        return "safety"
+    if "SMOKE" in zone["name"]:
+        return "smoke"
+    if "WATER" in zone["name"]:
+        return "water"
+    return "opening"
+
+
+class Concord232ZoneSensor(BinarySensorDevice):
+    """Representation of a Concord232 zone as a sensor."""
+
+    def __init__(self, hass, client, zone, zone_type):
+        """Initialize the Concord232 binary sensor."""
+        self._hass = hass
+        self._client = client
+        self._zone = zone
+        self._number = zone['number']
+        self._zone_type = zone_type
+        self.update()
+
+    @property
+    def sensor_class(self):
+        """Return the class of this sensor, from SENSOR_CLASSES."""
+        return self._zone_type
+
+    @property
+    def should_poll(self):
+        """No polling needed."""
+        return True
+
+    @property
+    def name(self):
+        """Return the name of the binary sensor."""
+        return self._zone['name']
+
+    @property
+    def is_on(self):
+        """Return true if the binary sensor is on."""
+        # True means "faulted" or "open" or "abnormal state"
+        return bool(self._zone['state'] == 'Normal')
+
+    def update(self):
+        """"Get updated stats from API."""
+        last_update = datetime.datetime.now() - self._client.last_zone_update
+        _LOGGER.debug("Zone: %s ", self._zone)
+        if last_update > datetime.timedelta(seconds=1):
+            self._client.zones = self._client.list_zones()
+            self._client.last_zone_update = datetime.datetime.now()
+            _LOGGER.debug("Updated from Zone: %s", self._zone['name'])
+
+        if hasattr(self._client, 'zones'):
+            self._zone = next((x for x in self._client.zones
+                               if x['number'] == self._number), None)
diff --git a/requirements_all.txt b/requirements_all.txt
index 240972252ac2ec9a8a4e4e00136b11af482d3dc6..5c44237bb83cee4b9b1f71b8a74a9e57bebd93d0 100644
--- a/requirements_all.txt
+++ b/requirements_all.txt
@@ -72,6 +72,10 @@ coinmarketcap==2.0.1
 # homeassistant.scripts.check_config
 colorlog>2.1,<3
 
+# homeassistant.components.alarm_control_panel.concord232
+# homeassistant.components.binary_sensor.concord232
+concord232==0.14
+
 # homeassistant.components.media_player.directv
 directpy==0.1