From 45fdda3f5d3d4fbaa8af7843be38737581c29cb5 Mon Sep 17 00:00:00 2001 From: Otto Winter Date: Sat, 29 Sep 2018 21:22:57 +0200 Subject: [PATCH] Add unique_id to MQTT fan (#16949) --- homeassistant/components/fan/mqtt.py | 13 ++++++++++++- tests/components/fan/test_mqtt.py | 29 ++++++++++++++++++++++++++-- 2 files changed, 39 insertions(+), 3 deletions(-) diff --git a/homeassistant/components/fan/mqtt.py b/homeassistant/components/fan/mqtt.py index 4ff8e1ec757..3e1ad2704e7 100644 --- a/homeassistant/components/fan/mqtt.py +++ b/homeassistant/components/fan/mqtt.py @@ -5,6 +5,7 @@ For more details about this platform, please refer to the documentation https://home-assistant.io/components/fan.mqtt/ """ import logging +from typing import Optional import voluptuous as vol @@ -41,6 +42,7 @@ CONF_PAYLOAD_LOW_SPEED = 'payload_low_speed' CONF_PAYLOAD_MEDIUM_SPEED = 'payload_medium_speed' CONF_PAYLOAD_HIGH_SPEED = 'payload_high_speed' CONF_SPEED_LIST = 'speeds' +CONF_UNIQUE_ID = 'unique_id' DEFAULT_NAME = 'MQTT Fan' DEFAULT_PAYLOAD_ON = 'ON' @@ -74,6 +76,7 @@ PLATFORM_SCHEMA = mqtt.MQTT_RW_PLATFORM_SCHEMA.extend({ default=[SPEED_OFF, SPEED_LOW, SPEED_MEDIUM, SPEED_HIGH]): cv.ensure_list, vol.Optional(CONF_OPTIMISTIC, default=DEFAULT_OPTIMISTIC): cv.boolean, + vol.Optional(CONF_UNIQUE_ID): cv.string, }).extend(mqtt.MQTT_AVAILABILITY_SCHEMA.schema) @@ -120,6 +123,7 @@ async def async_setup_platform(hass: HomeAssistantType, config: ConfigType, config.get(CONF_AVAILABILITY_TOPIC), config.get(CONF_PAYLOAD_AVAILABLE), config.get(CONF_PAYLOAD_NOT_AVAILABLE), + config.get(CONF_UNIQUE_ID), discovery_hash, )]) @@ -129,7 +133,8 @@ class MqttFan(MqttAvailability, MqttDiscoveryUpdate, FanEntity): def __init__(self, name, topic, templates, qos, retain, payload, speed_list, optimistic, availability_topic, payload_available, - payload_not_available, discovery_hash): + payload_not_available, unique_id: Optional[str], + discovery_hash): """Initialize the MQTT fan.""" MqttAvailability.__init__(self, availability_topic, qos, payload_available, payload_not_available) @@ -154,6 +159,7 @@ class MqttFan(MqttAvailability, MqttDiscoveryUpdate, FanEntity): is not None and SUPPORT_OSCILLATE) self._supported_features |= (topic[CONF_SPEED_STATE_TOPIC] is not None and SUPPORT_SET_SPEED) + self._unique_id = unique_id self._discovery_hash = discovery_hash async def async_added_to_hass(self): @@ -323,3 +329,8 @@ class MqttFan(MqttAvailability, MqttDiscoveryUpdate, FanEntity): if self._optimistic_oscillation: self._oscillation = oscillating self.async_schedule_update_ha_state() + + @property + def unique_id(self): + """Return a unique ID.""" + return self._unique_id diff --git a/tests/components/fan/test_mqtt.py b/tests/components/fan/test_mqtt.py index 5f59ebe4a76..7434e5aa1c9 100644 --- a/tests/components/fan/test_mqtt.py +++ b/tests/components/fan/test_mqtt.py @@ -1,14 +1,14 @@ """Test MQTT fans.""" import unittest -from homeassistant.setup import setup_component +from homeassistant.setup import setup_component, async_setup_component from homeassistant.components import fan from homeassistant.components.mqtt.discovery import async_start from homeassistant.const import ATTR_ASSUMED_STATE, STATE_UNAVAILABLE from tests.common import ( mock_mqtt_component, async_fire_mqtt_message, fire_mqtt_message, - get_test_home_assistant) + get_test_home_assistant, async_mock_mqtt_component) class TestMqttFan(unittest.TestCase): @@ -125,3 +125,28 @@ async def test_discovery_removal_fan(hass, mqtt_mock, caplog): await hass.async_block_till_done() state = hass.states.get('fan.beer') assert state is None + + +async def test_unique_id(hass): + """Test unique_id option only creates one fan per id.""" + await async_mock_mqtt_component(hass) + assert await async_setup_component(hass, fan.DOMAIN, { + fan.DOMAIN: [{ + 'platform': 'mqtt', + 'name': 'Test 1', + 'state_topic': 'test-topic', + 'command_topic': 'test-topic', + 'unique_id': 'TOTALLY_UNIQUE' + }, { + 'platform': 'mqtt', + 'name': 'Test 2', + 'state_topic': 'test-topic', + 'command_topic': 'test-topic', + 'unique_id': 'TOTALLY_UNIQUE' + }] + }) + + async_fire_mqtt_message(hass, 'test-topic', 'payload') + await hass.async_block_till_done() + + assert len(hass.states.async_entity_ids(fan.DOMAIN)) == 1