diff --git a/homeassistant/components/nexia/__init__.py b/homeassistant/components/nexia/__init__.py
index 41ecf6f1045b3311e34568014fae9f6bc91c8a02..5c317794c2aedcb7f2102a006e9ce5aaf2f3df2b 100644
--- a/homeassistant/components/nexia/__init__.py
+++ b/homeassistant/components/nexia/__init__.py
@@ -15,7 +15,7 @@ from homeassistant.exceptions import ConfigEntryNotReady
 import homeassistant.helpers.config_validation as cv
 from homeassistant.helpers.update_coordinator import DataUpdateCoordinator
 
-from .const import DATA_NEXIA, DOMAIN, NEXIA_DEVICE, PLATFORMS, UPDATE_COORDINATOR
+from .const import DOMAIN, NEXIA_DEVICE, PLATFORMS, UPDATE_COORDINATOR
 
 _LOGGER = logging.getLogger(__name__)
 
@@ -94,8 +94,7 @@ async def async_setup_entry(hass: HomeAssistant, entry: ConfigEntry):
         update_interval=timedelta(seconds=DEFAULT_UPDATE_RATE),
     )
 
-    hass.data[DOMAIN][entry.entry_id] = {}
-    hass.data[DOMAIN][entry.entry_id][DATA_NEXIA] = {
+    hass.data[DOMAIN][entry.entry_id] = {
         NEXIA_DEVICE: nexia_home,
         UPDATE_COORDINATOR: coordinator,
     }
diff --git a/homeassistant/components/nexia/binary_sensor.py b/homeassistant/components/nexia/binary_sensor.py
index 2802c3d7bd472db087bd995ab1f50bfddd2ed8e6..5c33412c647421820f75abf15c577fed7758bb3c 100644
--- a/homeassistant/components/nexia/binary_sensor.py
+++ b/homeassistant/components/nexia/binary_sensor.py
@@ -1,23 +1,15 @@
 """Support for Nexia / Trane XL Thermostats."""
 
 from homeassistant.components.binary_sensor import BinarySensorDevice
-from homeassistant.const import ATTR_ATTRIBUTION
 
-from .const import (
-    ATTRIBUTION,
-    DATA_NEXIA,
-    DOMAIN,
-    MANUFACTURER,
-    NEXIA_DEVICE,
-    UPDATE_COORDINATOR,
-)
-from .entity import NexiaEntity
+from .const import DOMAIN, NEXIA_DEVICE, UPDATE_COORDINATOR
+from .entity import NexiaThermostatEntity
 
 
 async def async_setup_entry(hass, config_entry, async_add_entities):
     """Set up sensors for a Nexia device."""
 
-    nexia_data = hass.data[DOMAIN][config_entry.entry_id][DATA_NEXIA]
+    nexia_data = hass.data[DOMAIN][config_entry.entry_id]
     nexia_home = nexia_data[NEXIA_DEVICE]
     coordinator = nexia_data[UPDATE_COORDINATOR]
 
@@ -42,48 +34,21 @@ async def async_setup_entry(hass, config_entry, async_add_entities):
     async_add_entities(entities, True)
 
 
-class NexiaBinarySensor(NexiaEntity, BinarySensorDevice):
+class NexiaBinarySensor(NexiaThermostatEntity, BinarySensorDevice):
     """Provices Nexia BinarySensor support."""
 
-    def __init__(self, coordinator, device, sensor_call, sensor_name):
+    def __init__(self, coordinator, thermostat, sensor_call, sensor_name):
         """Initialize the nexia sensor."""
-        super().__init__(coordinator)
-        self._coordinator = coordinator
-        self._device = device
-        self._name = f"{self._device.get_name()} {sensor_name}"
+        super().__init__(
+            coordinator,
+            thermostat,
+            name=f"{thermostat.get_name()} {sensor_name}",
+            unique_id=f"{thermostat.thermostat_id}_{sensor_call}",
+        )
         self._call = sensor_call
-        self._unique_id = f"{self._device.thermostat_id}_{sensor_call}"
         self._state = None
 
-    @property
-    def unique_id(self):
-        """Return the unique id of the binary sensor."""
-        return self._unique_id
-
-    @property
-    def name(self):
-        """Return the name of the sensor."""
-        return self._name
-
-    @property
-    def device_info(self):
-        """Return the device_info of the device."""
-        return {
-            "identifiers": {(DOMAIN, self._device.thermostat_id)},
-            "name": self._device.get_name(),
-            "model": self._device.get_model(),
-            "sw_version": self._device.get_firmware(),
-            "manufacturer": MANUFACTURER,
-        }
-
-    @property
-    def device_state_attributes(self):
-        """Return the device specific state attributes."""
-        return {
-            ATTR_ATTRIBUTION: ATTRIBUTION,
-        }
-
     @property
     def is_on(self):
         """Return the status of the sensor."""
-        return getattr(self._device, self._call)()
+        return getattr(self._thermostat, self._call)()
diff --git a/homeassistant/components/nexia/climate.py b/homeassistant/components/nexia/climate.py
index 7231f2b8ba9d97930fcf0631739c7d78f13682d9..8af1be20b1e95ebce46884b7bdd4030d771292e2 100644
--- a/homeassistant/components/nexia/climate.py
+++ b/homeassistant/components/nexia/climate.py
@@ -12,9 +12,11 @@ from nexia.const import (
     SYSTEM_STATUS_IDLE,
     UNIT_FAHRENHEIT,
 )
+import voluptuous as vol
 
 from homeassistant.components.climate import ClimateDevice
 from homeassistant.components.climate.const import (
+    ATTR_HUMIDITY,
     ATTR_MAX_HUMIDITY,
     ATTR_MIN_HUMIDITY,
     ATTR_TARGET_TEMP_HIGH,
@@ -36,26 +38,50 @@ from homeassistant.components.climate.const import (
     SUPPORT_TARGET_TEMPERATURE_RANGE,
 )
 from homeassistant.const import (
-    ATTR_ATTRIBUTION,
+    ATTR_ENTITY_ID,
     ATTR_TEMPERATURE,
     TEMP_CELSIUS,
     TEMP_FAHRENHEIT,
 )
+from homeassistant.helpers import entity_platform
+import homeassistant.helpers.config_validation as cv
+from homeassistant.helpers.dispatcher import dispatcher_send
 
 from .const import (
+    ATTR_AIRCLEANER_MODE,
     ATTR_DEHUMIDIFY_SETPOINT,
     ATTR_DEHUMIDIFY_SUPPORTED,
     ATTR_HUMIDIFY_SETPOINT,
     ATTR_HUMIDIFY_SUPPORTED,
     ATTR_ZONE_STATUS,
-    ATTRIBUTION,
-    DATA_NEXIA,
     DOMAIN,
-    MANUFACTURER,
     NEXIA_DEVICE,
+    SIGNAL_THERMOSTAT_UPDATE,
+    SIGNAL_ZONE_UPDATE,
     UPDATE_COORDINATOR,
 )
-from .entity import NexiaEntity
+from .entity import NexiaThermostatZoneEntity
+from .util import percent_conv
+
+SERVICE_SET_AIRCLEANER_MODE = "set_aircleaner_mode"
+SERVICE_SET_HUMIDIFY_SETPOINT = "set_humidify_setpoint"
+
+SET_AIRCLEANER_SCHEMA = vol.Schema(
+    {
+        vol.Required(ATTR_ENTITY_ID): cv.entity_ids,
+        vol.Required(ATTR_AIRCLEANER_MODE): cv.string,
+    }
+)
+
+SET_HUMIDITY_SCHEMA = vol.Schema(
+    {
+        vol.Required(ATTR_ENTITY_ID): cv.entity_ids,
+        vol.Required(ATTR_HUMIDITY): vol.All(
+            vol.Coerce(int), vol.Range(min=35, max=65)
+        ),
+    }
+)
+
 
 _LOGGER = logging.getLogger(__name__)
 
@@ -83,10 +109,21 @@ NEXIA_TO_HA_HVAC_MODE_MAP = {
 async def async_setup_entry(hass, config_entry, async_add_entities):
     """Set up climate for a Nexia device."""
 
-    nexia_data = hass.data[DOMAIN][config_entry.entry_id][DATA_NEXIA]
+    nexia_data = hass.data[DOMAIN][config_entry.entry_id]
     nexia_home = nexia_data[NEXIA_DEVICE]
     coordinator = nexia_data[UPDATE_COORDINATOR]
 
+    platform = entity_platform.current_platform.get()
+
+    platform.async_register_entity_service(
+        SERVICE_SET_HUMIDIFY_SETPOINT,
+        SET_HUMIDITY_SCHEMA,
+        SERVICE_SET_HUMIDIFY_SETPOINT,
+    )
+    platform.async_register_entity_service(
+        SERVICE_SET_AIRCLEANER_MODE, SET_AIRCLEANER_SCHEMA, SERVICE_SET_AIRCLEANER_MODE,
+    )
+
     entities = []
     for thermostat_id in nexia_home.get_thermostat_ids():
         thermostat = nexia_home.get_thermostat_by_id(thermostat_id)
@@ -97,26 +134,22 @@ async def async_setup_entry(hass, config_entry, async_add_entities):
     async_add_entities(entities, True)
 
 
-class NexiaZone(NexiaEntity, ClimateDevice):
+class NexiaZone(NexiaThermostatZoneEntity, ClimateDevice):
     """Provides Nexia Climate support."""
 
-    def __init__(self, coordinator, device):
+    def __init__(self, coordinator, zone):
         """Initialize the thermostat."""
-        super().__init__(coordinator)
-        self.thermostat = device.thermostat
-        self._device = device
-        self._coordinator = coordinator
+        super().__init__(
+            coordinator, zone, name=zone.get_name(), unique_id=zone.zone_id
+        )
+        self._undo_humidfy_dispatcher = None
+        self._undo_aircleaner_dispatcher = None
         # The has_* calls are stable for the life of the device
         # and do not do I/O
-        self._has_relative_humidity = self.thermostat.has_relative_humidity()
-        self._has_emergency_heat = self.thermostat.has_emergency_heat()
-        self._has_humidify_support = self.thermostat.has_humidify_support()
-        self._has_dehumidify_support = self.thermostat.has_dehumidify_support()
-
-    @property
-    def unique_id(self):
-        """Device Uniqueid."""
-        return self._device.zone_id
+        self._has_relative_humidity = self._thermostat.has_relative_humidity()
+        self._has_emergency_heat = self._thermostat.has_emergency_heat()
+        self._has_humidify_support = self._thermostat.has_humidify_support()
+        self._has_dehumidify_support = self._thermostat.has_dehumidify_support()
 
     @property
     def supported_features(self):
@@ -139,27 +172,22 @@ class NexiaZone(NexiaEntity, ClimateDevice):
     @property
     def is_fan_on(self):
         """Blower is on."""
-        return self.thermostat.is_blower_active()
-
-    @property
-    def name(self):
-        """Name of the zone."""
-        return self._device.get_name()
+        return self._thermostat.is_blower_active()
 
     @property
     def temperature_unit(self):
         """Return the unit of measurement."""
-        return TEMP_CELSIUS if self.thermostat.get_unit() == "C" else TEMP_FAHRENHEIT
+        return TEMP_CELSIUS if self._thermostat.get_unit() == "C" else TEMP_FAHRENHEIT
 
     @property
     def current_temperature(self):
         """Return the current temperature."""
-        return self._device.get_temperature()
+        return self._zone.get_temperature()
 
     @property
     def fan_mode(self):
         """Return the fan setting."""
-        return self.thermostat.get_fan_mode()
+        return self._thermostat.get_fan_mode()
 
     @property
     def fan_modes(self):
@@ -169,92 +197,92 @@ class NexiaZone(NexiaEntity, ClimateDevice):
     @property
     def min_temp(self):
         """Minimum temp for the current setting."""
-        return (self._device.thermostat.get_setpoint_limits())[0]
+        return (self._thermostat.get_setpoint_limits())[0]
 
     @property
     def max_temp(self):
         """Maximum temp for the current setting."""
-        return (self._device.thermostat.get_setpoint_limits())[1]
+        return (self._thermostat.get_setpoint_limits())[1]
 
     def set_fan_mode(self, fan_mode):
         """Set new target fan mode."""
-        self.thermostat.set_fan_mode(fan_mode)
-        self.schedule_update_ha_state()
+        self._thermostat.set_fan_mode(fan_mode)
+        self._signal_thermostat_update()
 
     @property
     def preset_mode(self):
         """Preset that is active."""
-        return self._device.get_preset()
+        return self._zone.get_preset()
 
     @property
     def preset_modes(self):
         """All presets."""
-        return self._device.get_presets()
+        return self._zone.get_presets()
 
     def set_humidity(self, humidity):
         """Dehumidify target."""
-        self.thermostat.set_dehumidify_setpoint(humidity / 100.0)
-        self.schedule_update_ha_state()
+        self._thermostat.set_dehumidify_setpoint(humidity / 100.0)
+        self._signal_thermostat_update()
 
     @property
     def target_humidity(self):
         """Humidity indoors setpoint."""
         if self._has_dehumidify_support:
-            return round(self.thermostat.get_dehumidify_setpoint() * 100.0, 1)
+            return percent_conv(self._thermostat.get_dehumidify_setpoint())
         if self._has_humidify_support:
-            return round(self.thermostat.get_humidify_setpoint() * 100.0, 1)
+            return percent_conv(self._thermostat.get_humidify_setpoint())
         return None
 
     @property
     def current_humidity(self):
         """Humidity indoors."""
         if self._has_relative_humidity:
-            return round(self.thermostat.get_relative_humidity() * 100.0, 1)
+            return percent_conv(self._thermostat.get_relative_humidity())
         return None
 
     @property
     def target_temperature(self):
         """Temperature we try to reach."""
-        current_mode = self._device.get_current_mode()
+        current_mode = self._zone.get_current_mode()
 
         if current_mode == OPERATION_MODE_COOL:
-            return self._device.get_cooling_setpoint()
+            return self._zone.get_cooling_setpoint()
         if current_mode == OPERATION_MODE_HEAT:
-            return self._device.get_heating_setpoint()
+            return self._zone.get_heating_setpoint()
         return None
 
     @property
     def target_temperature_step(self):
         """Step size of temperature units."""
-        if self._device.thermostat.get_unit() == UNIT_FAHRENHEIT:
+        if self._thermostat.get_unit() == UNIT_FAHRENHEIT:
             return 1.0
         return 0.5
 
     @property
     def target_temperature_high(self):
         """Highest temperature we are trying to reach."""
-        current_mode = self._device.get_current_mode()
+        current_mode = self._zone.get_current_mode()
 
         if current_mode in (OPERATION_MODE_COOL, OPERATION_MODE_HEAT):
             return None
-        return self._device.get_cooling_setpoint()
+        return self._zone.get_cooling_setpoint()
 
     @property
     def target_temperature_low(self):
         """Lowest temperature we are trying to reach."""
-        current_mode = self._device.get_current_mode()
+        current_mode = self._zone.get_current_mode()
 
         if current_mode in (OPERATION_MODE_COOL, OPERATION_MODE_HEAT):
             return None
-        return self._device.get_heating_setpoint()
+        return self._zone.get_heating_setpoint()
 
     @property
     def hvac_action(self) -> str:
         """Operation ie. heat, cool, idle."""
-        system_status = self.thermostat.get_system_status()
-        zone_called = self._device.is_calling()
+        system_status = self._thermostat.get_system_status()
+        zone_called = self._zone.is_calling()
 
-        if self._device.get_requested_mode() == OPERATION_MODE_OFF:
+        if self._zone.get_requested_mode() == OPERATION_MODE_OFF:
             return CURRENT_HVAC_OFF
         if not zone_called:
             return CURRENT_HVAC_IDLE
@@ -269,8 +297,8 @@ class NexiaZone(NexiaEntity, ClimateDevice):
     @property
     def hvac_mode(self):
         """Return current mode, as the user-visible name."""
-        mode = self._device.get_requested_mode()
-        hold = self._device.is_in_permanent_hold()
+        mode = self._zone.get_requested_mode()
+        hold = self._zone.is_in_permanent_hold()
 
         # If the device is in hold mode with
         # OPERATION_MODE_AUTO
@@ -299,10 +327,10 @@ class NexiaZone(NexiaEntity, ClimateDevice):
         new_cool_temp = kwargs.get(ATTR_TARGET_TEMP_HIGH, None)
         set_temp = kwargs.get(ATTR_TEMPERATURE, None)
 
-        deadband = self.thermostat.get_deadband()
-        cur_cool_temp = self._device.get_cooling_setpoint()
-        cur_heat_temp = self._device.get_heating_setpoint()
-        (min_temp, max_temp) = self.thermostat.get_setpoint_limits()
+        deadband = self._thermostat.get_deadband()
+        cur_cool_temp = self._zone.get_cooling_setpoint()
+        cur_heat_temp = self._zone.get_heating_setpoint()
+        (min_temp, max_temp) = self._thermostat.get_setpoint_limits()
 
         # Check that we're not going to hit any minimum or maximum values
         if new_heat_temp and new_heat_temp + deadband > max_temp:
@@ -318,114 +346,119 @@ class NexiaZone(NexiaEntity, ClimateDevice):
             if new_cool_temp - new_heat_temp < deadband:
                 new_heat_temp = new_cool_temp - deadband
 
-        self._device.set_heat_cool_temp(
+        self._zone.set_heat_cool_temp(
             heat_temperature=new_heat_temp,
             cool_temperature=new_cool_temp,
             set_temperature=set_temp,
         )
-        self.schedule_update_ha_state()
+        self._signal_zone_update()
 
     @property
     def is_aux_heat(self):
         """Emergency heat state."""
-        return self.thermostat.is_emergency_heat_active()
-
-    @property
-    def device_info(self):
-        """Return the device_info of the device."""
-        return {
-            "identifiers": {(DOMAIN, self._device.zone_id)},
-            "name": self._device.get_name(),
-            "model": self.thermostat.get_model(),
-            "sw_version": self.thermostat.get_firmware(),
-            "manufacturer": MANUFACTURER,
-            "via_device": (DOMAIN, self.thermostat.thermostat_id),
-        }
+        return self._thermostat.is_emergency_heat_active()
 
     @property
     def device_state_attributes(self):
         """Return the device specific state attributes."""
-        data = {
-            ATTR_ATTRIBUTION: ATTRIBUTION,
-            ATTR_ZONE_STATUS: self._device.get_status(),
-        }
+        data = super().device_state_attributes
+
+        data[ATTR_ZONE_STATUS] = self._zone.get_status()
+
+        if not self._has_relative_humidity:
+            return data
+
+        min_humidity = percent_conv(self._thermostat.get_humidity_setpoint_limits()[0])
+        max_humidity = percent_conv(self._thermostat.get_humidity_setpoint_limits()[1])
+        data.update(
+            {
+                ATTR_MIN_HUMIDITY: min_humidity,
+                ATTR_MAX_HUMIDITY: max_humidity,
+                ATTR_DEHUMIDIFY_SUPPORTED: self._has_dehumidify_support,
+                ATTR_HUMIDIFY_SUPPORTED: self._has_humidify_support,
+            }
+        )
 
-        if self._has_relative_humidity:
-            data.update(
-                {
-                    ATTR_HUMIDIFY_SUPPORTED: self._has_humidify_support,
-                    ATTR_DEHUMIDIFY_SUPPORTED: self._has_dehumidify_support,
-                    ATTR_MIN_HUMIDITY: round(
-                        self.thermostat.get_humidity_setpoint_limits()[0] * 100.0, 1,
-                    ),
-                    ATTR_MAX_HUMIDITY: round(
-                        self.thermostat.get_humidity_setpoint_limits()[1] * 100.0, 1,
-                    ),
-                }
+        if self._has_dehumidify_support:
+            dehumdify_setpoint = percent_conv(
+                self._thermostat.get_dehumidify_setpoint()
             )
-            if self._has_dehumidify_support:
-                data.update(
-                    {
-                        ATTR_DEHUMIDIFY_SETPOINT: round(
-                            self.thermostat.get_dehumidify_setpoint() * 100.0, 1
-                        ),
-                    }
-                )
-            if self._has_humidify_support:
-                data.update(
-                    {
-                        ATTR_HUMIDIFY_SETPOINT: round(
-                            self.thermostat.get_humidify_setpoint() * 100.0, 1
-                        )
-                    }
-                )
+            data[ATTR_DEHUMIDIFY_SETPOINT] = dehumdify_setpoint
+
+        if self._has_humidify_support:
+            humdify_setpoint = percent_conv(self._thermostat.get_humidify_setpoint())
+            data[ATTR_HUMIDIFY_SETPOINT] = humdify_setpoint
+
         return data
 
     def set_preset_mode(self, preset_mode: str):
         """Set the preset mode."""
-        self._device.set_preset(preset_mode)
-        self.schedule_update_ha_state()
+        self._zone.set_preset(preset_mode)
+        self._signal_zone_update()
 
     def turn_aux_heat_off(self):
         """Turn. Aux Heat off."""
-        self.thermostat.set_emergency_heat(False)
-        self.schedule_update_ha_state()
+        self._thermostat.set_emergency_heat(False)
+        self._signal_thermostat_update()
 
     def turn_aux_heat_on(self):
         """Turn. Aux Heat on."""
-        self.thermostat.set_emergency_heat(True)
-        self.schedule_update_ha_state()
+        self._thermostat.set_emergency_heat(True)
+        self._signal_thermostat_update()
 
     def turn_off(self):
         """Turn. off the zone."""
         self.set_hvac_mode(OPERATION_MODE_OFF)
-        self.schedule_update_ha_state()
+        self._signal_zone_update()
 
     def turn_on(self):
         """Turn. on the zone."""
         self.set_hvac_mode(OPERATION_MODE_AUTO)
-        self.schedule_update_ha_state()
+        self._signal_zone_update()
 
     def set_hvac_mode(self, hvac_mode: str) -> None:
         """Set the system mode (Auto, Heat_Cool, Cool, Heat, etc)."""
         if hvac_mode == HVAC_MODE_AUTO:
-            self._device.call_return_to_schedule()
-            self._device.set_mode(mode=OPERATION_MODE_AUTO)
+            self._zone.call_return_to_schedule()
+            self._zone.set_mode(mode=OPERATION_MODE_AUTO)
         else:
-            self._device.call_permanent_hold()
-            self._device.set_mode(mode=HA_TO_NEXIA_HVAC_MODE_MAP[hvac_mode])
+            self._zone.call_permanent_hold()
+            self._zone.set_mode(mode=HA_TO_NEXIA_HVAC_MODE_MAP[hvac_mode])
 
         self.schedule_update_ha_state()
 
     def set_aircleaner_mode(self, aircleaner_mode):
         """Set the aircleaner mode."""
-        self.thermostat.set_air_cleaner(aircleaner_mode)
-        self.schedule_update_ha_state()
+        self._thermostat.set_air_cleaner(aircleaner_mode)
+        self._signal_thermostat_update()
 
-    def set_humidify_setpoint(self, humidify_setpoint):
+    def set_humidify_setpoint(self, humidity):
         """Set the humidify setpoint."""
-        self.thermostat.set_humidify_setpoint(humidify_setpoint / 100.0)
-        self.schedule_update_ha_state()
+        self._thermostat.set_humidify_setpoint(humidity / 100.0)
+        self._signal_thermostat_update()
+
+    def _signal_thermostat_update(self):
+        """Signal a thermostat update.
+
+        Whenever the underlying library does an action against
+        a thermostat, the data for the thermostat and all
+        connected zone is updated.
+
+        Update all the zones on the thermostat.
+        """
+        dispatcher_send(
+            self.hass, f"{SIGNAL_THERMOSTAT_UPDATE}-{self._thermostat.thermostat_id}"
+        )
+
+    def _signal_zone_update(self):
+        """Signal a zone update.
+
+        Whenever the underlying library does an action against
+        a zone, the data for the zone is updated.
+
+        Update a single zone.
+        """
+        dispatcher_send(self.hass, f"{SIGNAL_ZONE_UPDATE}-{self._zone.zone_id}")
 
     async def async_update(self):
         """Update the entity.
diff --git a/homeassistant/components/nexia/const.py b/homeassistant/components/nexia/const.py
index 384c3aad1b6a8733457ebef66e06f05d6096d9af..dbe7b71705c6ff2f9caadccc5cb501cae7781e02 100644
--- a/homeassistant/components/nexia/const.py
+++ b/homeassistant/components/nexia/const.py
@@ -7,7 +7,6 @@ ATTRIBUTION = "Data provided by mynexia.com"
 NOTIFICATION_ID = "nexia_notification"
 NOTIFICATION_TITLE = "Nexia Setup"
 
-DATA_NEXIA = "nexia"
 NEXIA_DEVICE = "device"
 NEXIA_SCAN_INTERVAL = "scan_interval"
 
@@ -16,6 +15,8 @@ DEFAULT_ENTITY_NAMESPACE = "nexia"
 
 ATTR_DESCRIPTION = "description"
 
+ATTR_AIRCLEANER_MODE = "aircleaner_mode"
+
 ATTR_ZONE_STATUS = "zone_status"
 ATTR_HUMIDIFY_SUPPORTED = "humidify_supported"
 ATTR_DEHUMIDIFY_SUPPORTED = "dehumidify_supported"
@@ -24,5 +25,7 @@ ATTR_DEHUMIDIFY_SETPOINT = "dehumidify_setpoint"
 
 UPDATE_COORDINATOR = "update_coordinator"
 
-
 MANUFACTURER = "Trane"
+
+SIGNAL_ZONE_UPDATE = "NEXIA_CLIMATE_ZONE_UPDATE"
+SIGNAL_THERMOSTAT_UPDATE = "NEXIA_CLIMATE_THERMOSTAT_UPDATE"
diff --git a/homeassistant/components/nexia/entity.py b/homeassistant/components/nexia/entity.py
index ec02a7e5f21efd744f04c5dea7835f0ed2aff7a2..60675cc5888eb11030bc884eee9fafdf198cd6bd 100644
--- a/homeassistant/components/nexia/entity.py
+++ b/homeassistant/components/nexia/entity.py
@@ -1,14 +1,26 @@
 """The nexia integration base entity."""
 
+from homeassistant.const import ATTR_ATTRIBUTION
+from homeassistant.helpers.dispatcher import async_dispatcher_connect
 from homeassistant.helpers.entity import Entity
 
+from .const import (
+    ATTRIBUTION,
+    DOMAIN,
+    MANUFACTURER,
+    SIGNAL_THERMOSTAT_UPDATE,
+    SIGNAL_ZONE_UPDATE,
+)
+
 
 class NexiaEntity(Entity):
     """Base class for nexia entities."""
 
-    def __init__(self, coordinator):
+    def __init__(self, coordinator, name, unique_id):
         """Initialize the entity."""
         super().__init__()
+        self._unique_id = unique_id
+        self._name = name
         self._coordinator = coordinator
 
     @property
@@ -16,6 +28,23 @@ class NexiaEntity(Entity):
         """Return True if entity is available."""
         return self._coordinator.last_update_success
 
+    @property
+    def unique_id(self):
+        """Return the unique id."""
+        return self._unique_id
+
+    @property
+    def name(self):
+        """Return the name."""
+        return self._name
+
+    @property
+    def device_state_attributes(self):
+        """Return the device specific state attributes."""
+        return {
+            ATTR_ATTRIBUTION: ATTRIBUTION,
+        }
+
     @property
     def should_poll(self):
         """Return False, updates are controlled via coordinator."""
@@ -28,3 +57,77 @@ class NexiaEntity(Entity):
     async def async_will_remove_from_hass(self):
         """Undo subscription."""
         self._coordinator.async_remove_listener(self.async_write_ha_state)
+
+
+class NexiaThermostatEntity(NexiaEntity):
+    """Base class for nexia devices attached to a thermostat."""
+
+    def __init__(self, coordinator, thermostat, name, unique_id):
+        """Initialize the entity."""
+        super().__init__(coordinator, name, unique_id)
+        self._thermostat = thermostat
+        self._thermostat_update_subscription = None
+
+    @property
+    def device_info(self):
+        """Return the device_info of the device."""
+        return {
+            "identifiers": {(DOMAIN, self._thermostat.thermostat_id)},
+            "name": self._thermostat.get_name(),
+            "model": self._thermostat.get_model(),
+            "sw_version": self._thermostat.get_firmware(),
+            "manufacturer": MANUFACTURER,
+        }
+
+    async def async_added_to_hass(self):
+        """Listen for signals for services."""
+        await super().async_added_to_hass()
+        self._thermostat_update_subscription = async_dispatcher_connect(
+            self.hass,
+            f"{SIGNAL_THERMOSTAT_UPDATE}-{self._thermostat.thermostat_id}",
+            self.async_write_ha_state,
+        )
+
+    async def async_will_remove_from_hass(self):
+        """Unsub from signals for services."""
+        await super().async_will_remove_from_hass()
+        if self._thermostat_update_subscription:
+            self._thermostat_update_subscription()
+
+
+class NexiaThermostatZoneEntity(NexiaThermostatEntity):
+    """Base class for nexia devices attached to a thermostat."""
+
+    def __init__(self, coordinator, zone, name, unique_id):
+        """Initialize the entity."""
+        super().__init__(coordinator, zone.thermostat, name, unique_id)
+        self._zone = zone
+        self._zone_update_subscription = None
+
+    @property
+    def device_info(self):
+        """Return the device_info of the device."""
+        data = super().device_info
+        data.update(
+            {
+                "identifiers": {(DOMAIN, self._zone.zone_id)},
+                "name": self._zone.get_name(),
+                "via_device": (DOMAIN, self._zone.thermostat.thermostat_id),
+            }
+        )
+        return data
+
+    async def async_added_to_hass(self):
+        """Listen for signals for services."""
+        await super().async_added_to_hass()
+        self._zone_update_subscription = async_dispatcher_connect(
+            self.hass,
+            f"{SIGNAL_ZONE_UPDATE}-{self._zone.zone_id}",
+            self.async_write_ha_state,
+        )
+
+    async def async_will_remove_from_hass(self):
+        """Unsub from signals for services."""
+        await super().async_will_remove_from_hass()
+        if self._zone_update_subscription:
+            self._zone_update_subscription()
diff --git a/homeassistant/components/nexia/scene.py b/homeassistant/components/nexia/scene.py
index 4489a4de274a9937439bee55375e3670cb8c5009..fb851618aecb5a97196bc8f24d890165e37289ac 100644
--- a/homeassistant/components/nexia/scene.py
+++ b/homeassistant/components/nexia/scene.py
@@ -1,23 +1,18 @@
 """Support for Nexia Automations."""
 
 from homeassistant.components.scene import Scene
-from homeassistant.const import ATTR_ATTRIBUTION
-
-from .const import (
-    ATTR_DESCRIPTION,
-    ATTRIBUTION,
-    DATA_NEXIA,
-    DOMAIN,
-    NEXIA_DEVICE,
-    UPDATE_COORDINATOR,
-)
+from homeassistant.helpers.event import async_call_later
+
+from .const import ATTR_DESCRIPTION, DOMAIN, NEXIA_DEVICE, UPDATE_COORDINATOR
 from .entity import NexiaEntity
 
+SCENE_ACTIVATION_TIME = 5
+
 
 async def async_setup_entry(hass, config_entry, async_add_entities):
     """Set up automations for a Nexia device."""
 
-    nexia_data = hass.data[DOMAIN][config_entry.entry_id][DATA_NEXIA]
+    nexia_data = hass.data[DOMAIN][config_entry.entry_id]
     nexia_home = nexia_data[NEXIA_DEVICE]
     coordinator = nexia_data[UPDATE_COORDINATOR]
     entities = []
@@ -36,33 +31,28 @@ class NexiaAutomationScene(NexiaEntity, Scene):
 
     def __init__(self, coordinator, automation):
         """Initialize the automation scene."""
-        super().__init__(coordinator)
+        super().__init__(
+            coordinator, name=automation.name, unique_id=automation.automation_id,
+        )
         self._automation = automation
 
-    @property
-    def unique_id(self):
-        """Return the unique id of the automation scene."""
-        # This is the automation unique_id
-        return self._automation.automation_id
-
-    @property
-    def name(self):
-        """Return the name of the automation scene."""
-        return self._automation.name
-
     @property
     def device_state_attributes(self):
         """Return the scene specific state attributes."""
-        return {
-            ATTR_ATTRIBUTION: ATTRIBUTION,
-            ATTR_DESCRIPTION: self._automation.description,
-        }
+        data = super().device_state_attributes
+        data[ATTR_DESCRIPTION] = self._automation.description
+        return data
 
     @property
     def icon(self):
         """Return the icon of the automation scene."""
         return "mdi:script-text-outline"
 
-    def activate(self):
+    async def async_activate(self):
         """Activate an automation scene."""
-        self._automation.activate()
+        await self.hass.async_add_executor_job(self._automation.activate)
+
+        async def refresh_callback(_):
+            await self._coordinator.async_refresh()
+
+        async_call_later(self.hass, SCENE_ACTIVATION_TIME, refresh_callback)
diff --git a/homeassistant/components/nexia/sensor.py b/homeassistant/components/nexia/sensor.py
index 251101ccb1ef03e6a87667fb6da662425efae43c..abbffa2b8449a3d5cb001c19ea4646fb6fff3eb9 100644
--- a/homeassistant/components/nexia/sensor.py
+++ b/homeassistant/components/nexia/sensor.py
@@ -3,7 +3,6 @@
 from nexia.const import UNIT_CELSIUS
 
 from homeassistant.const import (
-    ATTR_ATTRIBUTION,
     DEVICE_CLASS_HUMIDITY,
     DEVICE_CLASS_TEMPERATURE,
     TEMP_CELSIUS,
@@ -11,21 +10,15 @@ from homeassistant.const import (
     UNIT_PERCENTAGE,
 )
 
-from .const import (
-    ATTRIBUTION,
-    DATA_NEXIA,
-    DOMAIN,
-    MANUFACTURER,
-    NEXIA_DEVICE,
-    UPDATE_COORDINATOR,
-)
-from .entity import NexiaEntity
+from .const import DOMAIN, NEXIA_DEVICE, UPDATE_COORDINATOR
+from .entity import NexiaThermostatEntity, NexiaThermostatZoneEntity
+from .util import percent_conv
 
 
 async def async_setup_entry(hass, config_entry, async_add_entities):
     """Set up sensors for a Nexia device."""
 
-    nexia_data = hass.data[DOMAIN][config_entry.entry_id][DATA_NEXIA]
+    nexia_data = hass.data[DOMAIN][config_entry.entry_id]
     nexia_home = nexia_data[NEXIA_DEVICE]
     coordinator = nexia_data[UPDATE_COORDINATOR]
     entities = []
@@ -35,7 +28,7 @@ async def async_setup_entry(hass, config_entry, async_add_entities):
         thermostat = nexia_home.get_thermostat_by_id(thermostat_id)
 
         entities.append(
-            NexiaSensor(
+            NexiaThermostatSensor(
                 coordinator,
                 thermostat,
                 "get_system_status",
@@ -46,7 +39,7 @@ async def async_setup_entry(hass, config_entry, async_add_entities):
         )
         # Air cleaner
         entities.append(
-            NexiaSensor(
+            NexiaThermostatSensor(
                 coordinator,
                 thermostat,
                 "get_air_cleaner_mode",
@@ -58,7 +51,7 @@ async def async_setup_entry(hass, config_entry, async_add_entities):
         # Compressor Speed
         if thermostat.has_variable_speed_compressor():
             entities.append(
-                NexiaSensor(
+                NexiaThermostatSensor(
                     coordinator,
                     thermostat,
                     "get_current_compressor_speed",
@@ -69,7 +62,7 @@ async def async_setup_entry(hass, config_entry, async_add_entities):
                 )
             )
             entities.append(
-                NexiaSensor(
+                NexiaThermostatSensor(
                     coordinator,
                     thermostat,
                     "get_requested_compressor_speed",
@@ -87,7 +80,7 @@ async def async_setup_entry(hass, config_entry, async_add_entities):
                 else TEMP_FAHRENHEIT
             )
             entities.append(
-                NexiaSensor(
+                NexiaThermostatSensor(
                     coordinator,
                     thermostat,
                     "get_outdoor_temperature",
@@ -99,7 +92,7 @@ async def async_setup_entry(hass, config_entry, async_add_entities):
         # Relative Humidity
         if thermostat.has_relative_humidity():
             entities.append(
-                NexiaSensor(
+                NexiaThermostatSensor(
                     coordinator,
                     thermostat,
                     "get_relative_humidity",
@@ -120,7 +113,7 @@ async def async_setup_entry(hass, config_entry, async_add_entities):
             )
             # Temperature
             entities.append(
-                NexiaZoneSensor(
+                NexiaThermostatZoneSensor(
                     coordinator,
                     zone,
                     "get_temperature",
@@ -132,13 +125,13 @@ async def async_setup_entry(hass, config_entry, async_add_entities):
             )
             # Zone Status
             entities.append(
-                NexiaZoneSensor(
+                NexiaThermostatZoneSensor(
                     coordinator, zone, "get_status", "Zone Status", None, None,
                 )
             )
             # Setpoint Status
             entities.append(
-                NexiaZoneSensor(
+                NexiaThermostatZoneSensor(
                     coordinator,
                     zone,
                     "get_setpoint_status",
@@ -151,18 +144,13 @@ async def async_setup_entry(hass, config_entry, async_add_entities):
     async_add_entities(entities, True)
 
 
-def percent_conv(val):
-    """Convert an actual percentage (0.0-1.0) to 0-100 scale."""
-    return val * 100.0
-
-
-class NexiaSensor(NexiaEntity):
+class NexiaThermostatSensor(NexiaThermostatEntity):
     """Provides Nexia thermostat sensor support."""
 
     def __init__(
         self,
         coordinator,
-        device,
+        thermostat,
         sensor_call,
         sensor_name,
         sensor_class,
@@ -170,35 +158,18 @@ class NexiaSensor(NexiaEntity):
         modifier=None,
     ):
         """Initialize the sensor."""
-        super().__init__(coordinator)
-        self._coordinator = coordinator
-        self._device = device
+        super().__init__(
+            coordinator,
+            thermostat,
+            name=f"{thermostat.get_name()} {sensor_name}",
+            unique_id=f"{thermostat.thermostat_id}_{sensor_call}",
+        )
         self._call = sensor_call
-        self._sensor_name = sensor_name
         self._class = sensor_class
         self._state = None
-        self._name = f"{self._device.get_name()} {self._sensor_name}"
         self._unit_of_measurement = sensor_unit
         self._modifier = modifier
 
-    @property
-    def unique_id(self):
-        """Return the unique id of the sensor."""
-        # This is the thermostat unique_id
-        return f"{self._device.thermostat_id}_{self._call}"
-
-    @property
-    def name(self):
-        """Return the name of the sensor."""
-        return self._name
-
-    @property
-    def device_state_attributes(self):
-        """Return the device specific state attributes."""
-        return {
-            ATTR_ATTRIBUTION: ATTRIBUTION,
-        }
-
     @property
     def device_class(self):
         """Return the device class of the sensor."""
@@ -207,7 +178,7 @@ class NexiaSensor(NexiaEntity):
     @property
     def state(self):
         """Return the state of the sensor."""
-        val = getattr(self._device, self._call)()
+        val = getattr(self._thermostat, self._call)()
         if self._modifier:
             val = self._modifier(val)
         if isinstance(val, float):
@@ -219,25 +190,14 @@ class NexiaSensor(NexiaEntity):
         """Return the unit of measurement this sensor expresses itself in."""
         return self._unit_of_measurement
 
-    @property
-    def device_info(self):
-        """Return the device_info of the device."""
-        return {
-            "identifiers": {(DOMAIN, self._device.thermostat_id)},
-            "name": self._device.get_name(),
-            "model": self._device.get_model(),
-            "sw_version": self._device.get_firmware(),
-            "manufacturer": MANUFACTURER,
-        }
-
 
-class NexiaZoneSensor(NexiaSensor):
+class NexiaThermostatZoneSensor(NexiaThermostatZoneEntity):
     """Nexia Zone Sensor Support."""
 
     def __init__(
         self,
         coordinator,
-        device,
+        zone,
         sensor_call,
         sensor_name,
         sensor_class,
@@ -248,29 +208,32 @@ class NexiaZoneSensor(NexiaSensor):
 
         super().__init__(
             coordinator,
-            device,
-            sensor_call,
-            sensor_name,
-            sensor_class,
-            sensor_unit,
-            modifier,
+            zone,
+            name=f"{zone.get_name()} {sensor_name}",
+            unique_id=f"{zone.zone_id}_{sensor_call}",
         )
-        self._device = device
+        self._call = sensor_call
+        self._class = sensor_class
+        self._state = None
+        self._unit_of_measurement = sensor_unit
+        self._modifier = modifier
 
     @property
-    def unique_id(self):
-        """Return the unique id of the sensor."""
-        # This is the zone unique_id
-        return f"{self._device.zone_id}_{self._call}"
+    def device_class(self):
+        """Return the device class of the sensor."""
+        return self._class
 
     @property
-    def device_info(self):
-        """Return the device_info of the device."""
-        return {
-            "identifiers": {(DOMAIN, self._device.zone_id)},
-            "name": self._device.get_name(),
-            "model": self._device.thermostat.get_model(),
-            "sw_version": self._device.thermostat.get_firmware(),
-            "manufacturer": MANUFACTURER,
-            "via_device": (DOMAIN, self._device.thermostat.thermostat_id),
-        }
+    def state(self):
+        """Return the state of the sensor."""
+        val = getattr(self._zone, self._call)()
+        if self._modifier:
+            val = self._modifier(val)
+        if isinstance(val, float):
+            val = round(val, 1)
+        return val
+
+    @property
+    def unit_of_measurement(self):
+        """Return the unit of measurement this sensor expresses itself in."""
+        return self._unit_of_measurement
diff --git a/homeassistant/components/nexia/services.yaml b/homeassistant/components/nexia/services.yaml
new file mode 100644
index 0000000000000000000000000000000000000000..725b215da5a6325baf360964ac5f6587dd4e5d63
--- /dev/null
+++ b/homeassistant/components/nexia/services.yaml
@@ -0,0 +1,19 @@
+set_aircleaner_mode:
+  description: "The air cleaner mode."
+  fields:
+    entity_id:
+      description: "This setting will affect all zones connected to the thermostat."
+      example: climate.master_bedroom
+    aircleaner_mode:
+      description: "The air cleaner mode to set. Options include \"auto\", \"quick\", or \"allergy\"."
+      example: allergy
+
+set_humidify_setpoint:
+  description: "The humidification set point."
+  fields:
+    entity_id:
+      description: "This setting will affect all zones connected to the thermostat."
+      example: climate.master_bedroom
+    humidity:
+      description: "The humidification setpoint as an int, range 35-65."
+      example: 45
\ No newline at end of file
diff --git a/homeassistant/components/nexia/util.py b/homeassistant/components/nexia/util.py
new file mode 100644
index 0000000000000000000000000000000000000000..d2ff10c8d3491e060be3289f67a08717b0533679
--- /dev/null
+++ b/homeassistant/components/nexia/util.py
@@ -0,0 +1,6 @@
+"""Utils for Nexia / Trane XL Thermostats."""
+
+
+def percent_conv(val):
+    """Convert an actual percentage (0.0-1.0) to 0-100 scale."""
+    return round(val * 100.0, 1)
diff --git a/tests/components/nexia/test_binary_sensor.py b/tests/components/nexia/test_binary_sensor.py
new file mode 100644
index 0000000000000000000000000000000000000000..64b2946ee2feaf0491adc1dd4739b4345bc05373
--- /dev/null
+++ b/tests/components/nexia/test_binary_sensor.py
@@ -0,0 +1,35 @@
+"""The binary_sensor tests for the nexia platform."""
+
+from homeassistant.const import STATE_OFF, STATE_ON
+
+from .util import async_init_integration
+
+
+async def test_create_binary_sensors(hass):
+    """Test creation of binary sensors."""
+
+    await async_init_integration(hass)
+
+    state = hass.states.get("binary_sensor.master_suite_blower_active")
+    assert state.state == STATE_ON
+    expected_attributes = {
+        "attribution": "Data provided by mynexia.com",
+        "friendly_name": "Master Suite Blower Active",
+    }
+    # Only test for a subset of attributes in case
+    # HA changes the implementation and a new one appears
+    assert all(
+        state.attributes[key] == expected_attributes[key] for key in expected_attributes
+    )
+
+    state = hass.states.get("binary_sensor.downstairs_east_wing_blower_active")
+    assert state.state == STATE_OFF
+    expected_attributes = {
+        "attribution": "Data provided by mynexia.com",
+        "friendly_name": "Downstairs East Wing Blower Active",
+    }
+    # Only test for a subset of attributes in case
+    # HA changes the implementation and a new one appears
+    assert all(
+        state.attributes[key] == expected_attributes[key] for key in expected_attributes
+    )
diff --git a/tests/components/nexia/test_climate.py b/tests/components/nexia/test_climate.py
index 327c611d2774879988d8181985832eb0bf0109ab..e7675ff68b1bb9a60b9f6790a0c487019db2b681 100644
--- a/tests/components/nexia/test_climate.py
+++ b/tests/components/nexia/test_climate.py
@@ -43,3 +43,38 @@ async def test_climate_zones(hass):
     assert all(
         state.attributes[key] == expected_attributes[key] for key in expected_attributes
     )
+
+    state = hass.states.get("climate.kitchen")
+    assert state.state == HVAC_MODE_HEAT_COOL
+
+    expected_attributes = {
+        "attribution": "Data provided by mynexia.com",
+        "current_humidity": 36.0,
+        "current_temperature": 25.0,
+        "dehumidify_setpoint": 50.0,
+        "dehumidify_supported": True,
+        "fan_mode": "auto",
+        "fan_modes": ["auto", "on", "circulate"],
+        "friendly_name": "Kitchen",
+        "humidify_supported": False,
+        "humidity": 50.0,
+        "hvac_action": "idle",
+        "hvac_modes": ["off", "auto", "heat_cool", "heat", "cool"],
+        "max_humidity": 65.0,
+        "max_temp": 37.2,
+        "min_humidity": 35.0,
+        "min_temp": 12.8,
+        "preset_mode": "None",
+        "preset_modes": ["None", "Home", "Away", "Sleep"],
+        "supported_features": 31,
+        "target_temp_high": 26.1,
+        "target_temp_low": 17.2,
+        "target_temp_step": 1.0,
+        "temperature": None,
+        "zone_status": "",
+    }
+    # Only test for a subset of attributes in case
+    # HA changes the implementation and a new one appears
+    assert all(
+        state.attributes[key] == expected_attributes[key] for key in expected_attributes
+    )
diff --git a/tests/components/nexia/test_scene.py b/tests/components/nexia/test_scene.py
index e6a5e94f083a47a099d003d1153ef56a2329394e..4a325552e8032b92538ebbdd917d757a747821dc 100644
--- a/tests/components/nexia/test_scene.py
+++ b/tests/components/nexia/test_scene.py
@@ -1,10 +1,10 @@
-"""The lock tests for the august platform."""
+"""The scene tests for the nexia platform."""
 
 from .util import async_init_integration
 
 
-async def test_automation_scenees(hass):
-    """Test creation automation scenees."""
+async def test_automation_scenes(hass):
+    """Test creation automation scenes."""
 
     await async_init_integration(hass)
 
diff --git a/tests/components/nexia/test_sensor.py b/tests/components/nexia/test_sensor.py
new file mode 100644
index 0000000000000000000000000000000000000000..6e258d0ad55c111d6f8878812c157e8b72424186
--- /dev/null
+++ b/tests/components/nexia/test_sensor.py
@@ -0,0 +1,133 @@
+"""The sensor tests for the nexia platform."""
+
+from .util import async_init_integration
+
+
+async def test_create_sensors(hass):
+    """Test creation of sensors."""
+
+    await async_init_integration(hass)
+
+    state = hass.states.get("sensor.nick_office_temperature")
+    assert state.state == "23"
+
+    expected_attributes = {
+        "attribution": "Data provided by mynexia.com",
+        "device_class": "temperature",
+        "friendly_name": "Nick Office Temperature",
+        "unit_of_measurement": "°C",
+    }
+    # Only test for a subset of attributes in case
+    # HA changes the implementation and a new one appears
+    assert all(
+        state.attributes[key] == expected_attributes[key] for key in expected_attributes
+    )
+
+    state = hass.states.get("sensor.nick_office_zone_setpoint_status")
+    assert state.state == "Permanent Hold"
+    expected_attributes = {
+        "attribution": "Data provided by mynexia.com",
+        "friendly_name": "Nick Office Zone Setpoint Status",
+    }
+    # Only test for a subset of attributes in case
+    # HA changes the implementation and a new one appears
+    assert all(
+        state.attributes[key] == expected_attributes[key] for key in expected_attributes
+    )
+
+    state = hass.states.get("sensor.nick_office_zone_status")
+    assert state.state == "Relieving Air"
+
+    expected_attributes = {
+        "attribution": "Data provided by mynexia.com",
+        "friendly_name": "Nick Office Zone Status",
+    }
+    # Only test for a subset of attributes in case
+    # HA changes the implementation and a new one appears
+    assert all(
+        state.attributes[key] == expected_attributes[key] for key in expected_attributes
+    )
+
+    state = hass.states.get("sensor.master_suite_air_cleaner_mode")
+    assert state.state == "auto"
+
+    expected_attributes = {
+        "attribution": "Data provided by mynexia.com",
+        "friendly_name": "Master Suite Air Cleaner Mode",
+    }
+    # Only test for a subset of attributes in case
+    # HA changes the implementation and a new one appears
+    assert all(
+        state.attributes[key] == expected_attributes[key] for key in expected_attributes
+    )
+
+    state = hass.states.get("sensor.master_suite_current_compressor_speed")
+    assert state.state == "69.0"
+
+    expected_attributes = {
+        "attribution": "Data provided by mynexia.com",
+        "friendly_name": "Master Suite Current Compressor Speed",
+        "unit_of_measurement": "%",
+    }
+    # Only test for a subset of attributes in case
+    # HA changes the implementation and a new one appears
+    assert all(
+        state.attributes[key] == expected_attributes[key] for key in expected_attributes
+    )
+
+    state = hass.states.get("sensor.master_suite_outdoor_temperature")
+    assert state.state == "30.6"
+
+    expected_attributes = {
+        "attribution": "Data provided by mynexia.com",
+        "device_class": "temperature",
+        "friendly_name": "Master Suite Outdoor Temperature",
+        "unit_of_measurement": "°C",
+    }
+    # Only test for a subset of attributes in case
+    # HA changes the implementation and a new one appears
+    assert all(
+        state.attributes[key] == expected_attributes[key] for key in expected_attributes
+    )
+
+    state = hass.states.get("sensor.master_suite_relative_humidity")
+    assert state.state == "52.0"
+
+    expected_attributes = {
+        "attribution": "Data provided by mynexia.com",
+        "device_class": "humidity",
+        "friendly_name": "Master Suite Relative Humidity",
+        "unit_of_measurement": "%",
+    }
+    # Only test for a subset of attributes in case
+    # HA changes the implementation and a new one appears
+    assert all(
+        state.attributes[key] == expected_attributes[key] for key in expected_attributes
+    )
+
+    state = hass.states.get("sensor.master_suite_requested_compressor_speed")
+    assert state.state == "69.0"
+
+    expected_attributes = {
+        "attribution": "Data provided by mynexia.com",
+        "friendly_name": "Master Suite Requested Compressor Speed",
+        "unit_of_measurement": "%",
+    }
+    # Only test for a subset of attributes in case
+    # HA changes the implementation and a new one appears
+    assert all(
+        state.attributes[key] == expected_attributes[key] for key in expected_attributes
+    )
+
+    state = hass.states.get("sensor.master_suite_system_status")
+    assert state.state == "Cooling"
+
+    expected_attributes = {
+        "attribution": "Data provided by mynexia.com",
+        "friendly_name": "Master Suite System Status",
+    }
+    # Only test for a subset of attributes in case
+    # HA changes the implementation and a new one appears
+    assert all(
+        state.attributes[key] == expected_attributes[key] for key in expected_attributes
+    )