diff --git a/CODEOWNERS b/CODEOWNERS index 6983d13fc8b..60501876cd9 100644 --- a/CODEOWNERS +++ b/CODEOWNERS @@ -354,6 +354,7 @@ homeassistant/components/time_date/* @fabaff homeassistant/components/tmb/* @alemuro homeassistant/components/todoist/* @boralyl homeassistant/components/toon/* @frenck +homeassistant/components/totalconnect/* @austinmroczek homeassistant/components/tplink/* @rytilahti homeassistant/components/traccar/* @ludeeus homeassistant/components/tradfri/* @ggravlingen diff --git a/homeassistant/components/totalconnect/__init__.py b/homeassistant/components/totalconnect/__init__.py index 020f2d9c07f..e6cfbbc629a 100644 --- a/homeassistant/components/totalconnect/__init__.py +++ b/homeassistant/components/totalconnect/__init__.py @@ -24,7 +24,7 @@ CONFIG_SCHEMA = vol.Schema( extra=vol.ALLOW_EXTRA, ) -TOTALCONNECT_PLATFORMS = ["alarm_control_panel"] +TOTALCONNECT_PLATFORMS = ["alarm_control_panel", "binary_sensor"] def setup(hass, config): diff --git a/homeassistant/components/totalconnect/alarm_control_panel.py b/homeassistant/components/totalconnect/alarm_control_panel.py index ed77fc4eea0..b255132a365 100644 --- a/homeassistant/components/totalconnect/alarm_control_panel.py +++ b/homeassistant/components/totalconnect/alarm_control_panel.py @@ -32,10 +32,9 @@ def setup_platform(hass, config, add_entities, discovery_info=None): client = hass.data[TOTALCONNECT_DOMAIN].client - for location in client.locations: - location_id = location.get("LocationID") - name = location.get("LocationName") - alarms.append(TotalConnectAlarm(name, location_id, client)) + for location_id, location in client.locations.items(): + location_name = location.location_name + alarms.append(TotalConnectAlarm(location_name, location_id, client)) add_entities(alarms) @@ -72,35 +71,35 @@ class TotalConnectAlarm(alarm.AlarmControlPanel): def update(self): """Return the state of the device.""" - status = self._client.get_armed_status(self._name) + status = self._client.get_armed_status(self._location_id) attr = { "location_name": self._name, "location_id": self._location_id, - "ac_loss": self._client.ac_loss, - "low_battery": self._client.low_battery, + "ac_loss": self._client.locations[self._location_id].ac_loss, + "low_battery": self._client.locations[self._location_id].low_battery, + "cover_tampered": self._client.locations[ + self._location_id + ].is_cover_tampered, "triggered_source": None, "triggered_zone": None, } - if status == self._client.DISARMED: + if status in (self._client.DISARMED, self._client.DISARMED_BYPASS): state = STATE_ALARM_DISARMED - elif status == self._client.DISARMED_BYPASS: - state = STATE_ALARM_DISARMED - elif status == self._client.ARMED_STAY: - state = STATE_ALARM_ARMED_HOME - elif status == self._client.ARMED_STAY_INSTANT: - state = STATE_ALARM_ARMED_HOME - elif status == self._client.ARMED_STAY_INSTANT_BYPASS: + elif status in ( + self._client.ARMED_STAY, + self._client.ARMED_STAY_INSTANT, + self._client.ARMED_STAY_INSTANT_BYPASS, + ): state = STATE_ALARM_ARMED_HOME elif status == self._client.ARMED_STAY_NIGHT: state = STATE_ALARM_ARMED_NIGHT - elif status == self._client.ARMED_AWAY: - state = STATE_ALARM_ARMED_AWAY - elif status == self._client.ARMED_AWAY_BYPASS: - state = STATE_ALARM_ARMED_AWAY - elif status == self._client.ARMED_AWAY_INSTANT: - state = STATE_ALARM_ARMED_AWAY - elif status == self._client.ARMED_AWAY_INSTANT_BYPASS: + elif status in ( + self._client.ARMED_AWAY, + self._client.ARMED_AWAY_BYPASS, + self._client.ARMED_AWAY_INSTANT, + self._client.ARMED_AWAY_INSTANT_BYPASS, + ): state = STATE_ALARM_ARMED_AWAY elif status == self._client.ARMED_CUSTOM_BYPASS: state = STATE_ALARM_ARMED_CUSTOM_BYPASS @@ -128,16 +127,16 @@ class TotalConnectAlarm(alarm.AlarmControlPanel): def alarm_disarm(self, code=None): """Send disarm command.""" - self._client.disarm(self._name) + self._client.disarm(self._location_id) def alarm_arm_home(self, code=None): """Send arm home command.""" - self._client.arm_stay(self._name) + self._client.arm_stay(self._location_id) def alarm_arm_away(self, code=None): """Send arm away command.""" - self._client.arm_away(self._name) + self._client.arm_away(self._location_id) def alarm_arm_night(self, code=None): """Send arm night command.""" - self._client.arm_stay_night(self._name) + self._client.arm_stay_night(self._location_id) diff --git a/homeassistant/components/totalconnect/binary_sensor.py b/homeassistant/components/totalconnect/binary_sensor.py new file mode 100644 index 00000000000..28bd58cfff8 --- /dev/null +++ b/homeassistant/components/totalconnect/binary_sensor.py @@ -0,0 +1,90 @@ +"""Interfaces with TotalConnect sensors.""" +import logging + +from homeassistant.components.binary_sensor import ( + DEVICE_CLASS_DOOR, + DEVICE_CLASS_GAS, + DEVICE_CLASS_SMOKE, + BinarySensorDevice, +) + +from . import DOMAIN as TOTALCONNECT_DOMAIN + +_LOGGER = logging.getLogger(__name__) + + +def setup_platform(hass, config, add_entities, discovery_info=None): + """Set up a sensor for a TotalConnect device.""" + if discovery_info is None: + return + + sensors = [] + + client_locations = hass.data[TOTALCONNECT_DOMAIN].client.locations + + for location_id, location in client_locations.items(): + for zone_id, zone in location.zones.items(): + sensors.append(TotalConnectBinarySensor(zone_id, location_id, zone)) + add_entities(sensors, True) + + +class TotalConnectBinarySensor(BinarySensorDevice): + """Represent an TotalConnect zone.""" + + def __init__(self, zone_id, location_id, zone): + """Initialize the TotalConnect status.""" + self._zone_id = zone_id + self._location_id = location_id + self._zone = zone + self._name = self._zone.description + self._unique_id = f"{location_id} {zone_id}" + self._is_on = None + self._is_tampered = None + self._is_low_battery = None + + @property + def unique_id(self): + """Return the unique id.""" + return self._unique_id + + @property + def name(self): + """Return the name of the device.""" + return self._name + + def update(self): + """Return the state of the device.""" + self._is_tampered = self._zone.is_tampered() + self._is_low_battery = self._zone.is_low_battery() + + if self._zone.is_faulted() or self._zone.is_triggered(): + self._is_on = True + else: + self._is_on = False + + @property + def is_on(self): + """Return true if the binary sensor is on.""" + return self._is_on + + @property + def device_class(self): + """Return the class of this device, from component DEVICE_CLASSES.""" + if self._zone.is_type_security(): + return DEVICE_CLASS_DOOR + if self._zone.is_type_fire(): + return DEVICE_CLASS_SMOKE + if self._zone.is_type_carbon_monoxide(): + return DEVICE_CLASS_GAS + return None + + @property + def device_state_attributes(self): + """Return the state attributes.""" + attributes = { + "zone_id": self._zone_id, + "location_id": self._location_id, + "low_battery": self._is_low_battery, + "tampered": self._is_tampered, + } + return attributes diff --git a/homeassistant/components/totalconnect/manifest.json b/homeassistant/components/totalconnect/manifest.json index 6b2119f1cf5..967115e721a 100644 --- a/homeassistant/components/totalconnect/manifest.json +++ b/homeassistant/components/totalconnect/manifest.json @@ -2,7 +2,7 @@ "domain": "totalconnect", "name": "Honeywell Total Connect Alarm", "documentation": "https://www.home-assistant.io/integrations/totalconnect", - "requirements": ["total_connect_client==0.28"], + "requirements": ["total_connect_client==0.50"], "dependencies": [], - "codeowners": [] + "codeowners": ["@austinmroczek"] } diff --git a/requirements_all.txt b/requirements_all.txt index 8a43c7c8277..87a2b0d8f1c 100644 --- a/requirements_all.txt +++ b/requirements_all.txt @@ -1981,7 +1981,7 @@ todoist-python==8.0.0 toonapilib==3.2.4 # homeassistant.components.totalconnect -total_connect_client==0.28 +total_connect_client==0.50 # homeassistant.components.tplink_lte tp-connected==0.0.4