Skip to content

Commit

Permalink
Add backwards compatibility for MQTT discovered MQTT lights.
Browse files Browse the repository at this point in the history
Refactor according to review comments.
  • Loading branch information
emontnemery committed Nov 22, 2018
1 parent 9201c03 commit 4605e14
Show file tree
Hide file tree
Showing 8 changed files with 145 additions and 31 deletions.
44 changes: 27 additions & 17 deletions homeassistant/components/light/mqtt/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,9 @@
"""
import logging

from homeassistant.components import mqtt, light
import voluptuous as vol

from homeassistant.components import light
from homeassistant.components.mqtt import ATTR_DISCOVERY_HASH
from homeassistant.components.mqtt.discovery import MQTT_DISCOVERY_NEW
from homeassistant.helpers.dispatcher import async_dispatcher_connect
Expand All @@ -20,11 +22,23 @@

DEPENDENCIES = ['mqtt']

PLATFORM_SCHEMA = mqtt.MQTT_RW_PLATFORM_SCHEMA.extend(
schema_basic.PLATFORM_SCHEMA_BASIC.schema).extend(
schema_json.PLATFORM_SCHEMA_JSON.schema).extend(
schema_template.PLATFORM_SCHEMA_TEMPLATE.schema).extend(
mqtt.MQTT_AVAILABILITY_SCHEMA.schema)
CONF_SCHEMA = 'schema'


def validate_mqtt_light(value):
"""Validate MQTT light schema."""
schemas = {
'basic': schema_basic.PLATFORM_SCHEMA_BASIC,
'json': schema_json.PLATFORM_SCHEMA_JSON,
'template': schema_template.PLATFORM_SCHEMA_TEMPLATE,
}
return schemas[value[CONF_SCHEMA]](value)


PLATFORM_SCHEMA = vol.All(vol.Schema({
vol.Optional(CONF_SCHEMA, default='basic'): vol.All(
vol.Lower, vol.Any('basic', 'json', 'template'))
}, extra=vol.ALLOW_EXTRA), validate_mqtt_light)


async def async_setup_platform(hass: HomeAssistantType, config: ConfigType,
Expand All @@ -49,14 +63,10 @@ async def async_discover(discovery_payload):
async def _async_setup_entity(hass, config, async_add_entities,
discovery_hash=None):
"""Set up a MQTT Light."""
schema = config.get('schema', 'basic')

if schema == 'basic':
await schema_basic.async_setup_entity_basic(
hass, config, async_add_entities, discovery_hash)
elif schema == 'json':
await schema_json.async_setup_entity_json(
hass, config, async_add_entities, discovery_hash)
elif schema == 'template':
await schema_template.async_setup_entity_template(
hass, config, async_add_entities, discovery_hash)
setup_entity = {
'basic': schema_basic.async_setup_entity_basic,
'json': schema_json.async_setup_entity_json,
'template': schema_template.async_setup_entity_template,
}
await setup_entity[config['schema']](
hass, config, async_add_entities, discovery_hash)
4 changes: 2 additions & 2 deletions homeassistant/components/light/mqtt/schema_basic.py
Original file line number Diff line number Diff line change
Expand Up @@ -69,7 +69,7 @@

VALUES_ON_COMMAND_TYPE = ['first', 'last', 'brightness']

PLATFORM_SCHEMA_BASIC = vol.Schema({
PLATFORM_SCHEMA_BASIC = mqtt.MQTT_RW_PLATFORM_SCHEMA.extend({
vol.Optional(CONF_BRIGHTNESS_COMMAND_TOPIC): mqtt.valid_publish_topic,
vol.Optional(CONF_BRIGHTNESS_SCALE, default=DEFAULT_BRIGHTNESS_SCALE):
vol.All(vol.Coerce(int), vol.Range(min=1)),
Expand Down Expand Up @@ -105,7 +105,7 @@
vol.Optional(CONF_XY_VALUE_TEMPLATE): cv.template,
vol.Optional(CONF_ON_COMMAND_TYPE, default=DEFAULT_ON_COMMAND_TYPE):
vol.In(VALUES_ON_COMMAND_TYPE),
})
}).extend(mqtt.MQTT_AVAILABILITY_SCHEMA.schema)


async def async_setup_entity_basic(hass, config, async_add_entities,
Expand Down
4 changes: 2 additions & 2 deletions homeassistant/components/light/mqtt/schema_json.py
Original file line number Diff line number Diff line change
Expand Up @@ -58,7 +58,7 @@
CONF_UNIQUE_ID = 'unique_id'

# Stealing some of these from the base MQTT configs.
PLATFORM_SCHEMA_JSON = vol.Schema({
PLATFORM_SCHEMA_JSON = mqtt.MQTT_RW_PLATFORM_SCHEMA.extend({
vol.Optional(CONF_BRIGHTNESS, default=DEFAULT_BRIGHTNESS): cv.boolean,
vol.Optional(CONF_BRIGHTNESS_SCALE, default=DEFAULT_BRIGHTNESS_SCALE):
vol.All(vol.Coerce(int), vol.Range(min=1)),
Expand All @@ -81,7 +81,7 @@
vol.Optional(CONF_XY, default=DEFAULT_XY): cv.boolean,
vol.Optional(CONF_HS, default=DEFAULT_HS): cv.boolean,
vol.Required(CONF_COMMAND_TOPIC): mqtt.valid_publish_topic,
})
}).extend(mqtt.MQTT_AVAILABILITY_SCHEMA.schema)


async def async_setup_entity_json(hass: HomeAssistantType, config: ConfigType,
Expand Down
8 changes: 4 additions & 4 deletions homeassistant/components/light/mqtt/schema_template.py
Original file line number Diff line number Diff line change
Expand Up @@ -44,7 +44,7 @@
CONF_STATE_TEMPLATE = 'state_template'
CONF_WHITE_VALUE_TEMPLATE = 'white_value_template'

PLATFORM_SCHEMA_TEMPLATE = vol.Schema({
PLATFORM_SCHEMA_TEMPLATE = mqtt.MQTT_RW_PLATFORM_SCHEMA.extend({
vol.Optional(CONF_BLUE_TEMPLATE): cv.template,
vol.Optional(CONF_BRIGHTNESS_TEMPLATE): cv.template,
vol.Optional(CONF_COLOR_TEMP_TEMPLATE): cv.template,
Expand All @@ -58,12 +58,12 @@
vol.Optional(CONF_STATE_TEMPLATE): cv.template,
vol.Optional(CONF_STATE_TOPIC): mqtt.valid_subscribe_topic,
vol.Optional(CONF_WHITE_VALUE_TEMPLATE): cv.template,
vol.Optional(CONF_COMMAND_OFF_TEMPLATE): cv.template,
vol.Optional(CONF_COMMAND_ON_TEMPLATE): cv.template,
vol.Required(CONF_COMMAND_OFF_TEMPLATE): cv.template,
vol.Required(CONF_COMMAND_ON_TEMPLATE): cv.template,
vol.Required(CONF_COMMAND_TOPIC): mqtt.valid_publish_topic,
vol.Optional(CONF_QOS, default=mqtt.DEFAULT_QOS):
vol.All(vol.Coerce(int), vol.In([0, 1, 2])),
})
}).extend(mqtt.MQTT_AVAILABILITY_SCHEMA.schema)


async def async_setup_entity_template(hass, config, async_add_entities,
Expand Down
24 changes: 19 additions & 5 deletions homeassistant/components/mqtt/discovery.py
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,13 @@
'fan',
]

DEPRECATED_PLATFORM_TO_SCHEMA = {
'mqtt': 'basic',
'mqtt_json': 'json',
'mqtt_template': 'template',
}


ALREADY_DISCOVERED = 'mqtt_discovered_components'
DATA_CONFIG_ENTRY_LOCK = 'mqtt_config_entry_lock'
CONFIG_ENTRY_IS_SETUP = 'mqtt_config_entry_is_setup'
Expand Down Expand Up @@ -203,8 +210,15 @@ async def async_device_message_received(topic, payload, qos):
discovery_hash = (component, discovery_id)

if payload:
platform = 'mqtt'
payload[CONF_PLATFORM] = platform
if CONF_PLATFORM in payload:
platform = payload[CONF_PLATFORM]
if platform in DEPRECATED_PLATFORM_TO_SCHEMA:
schema = DEPRECATED_PLATFORM_TO_SCHEMA[platform]
payload['schema'] = schema
_LOGGER.warning('"platform": "%s" is deprecated, '
'replace with "schema":"%s"',
platform, schema)
payload[CONF_PLATFORM] = 'mqtt'

if CONF_STATE_TOPIC not in payload:
payload[CONF_STATE_TOPIC] = '{}/{}/{}{}/state'.format(
Expand All @@ -229,18 +243,18 @@ async def async_device_message_received(topic, payload, qos):

if component not in CONFIG_ENTRY_COMPONENTS:
await async_load_platform(
hass, component, platform, payload, hass_config)
hass, component, 'mqtt', payload, hass_config)
return

config_entries_key = '{}.{}'.format(component, platform)
config_entries_key = '{}.{}'.format(component, 'mqtt')
async with hass.data[DATA_CONFIG_ENTRY_LOCK]:
if config_entries_key not in hass.data[CONFIG_ENTRY_IS_SETUP]:
await hass.config_entries.async_forward_entry_setup(
config_entry, component)
hass.data[CONFIG_ENTRY_IS_SETUP].add(config_entries_key)

async_dispatcher_send(hass, MQTT_DISCOVERY_NEW.format(
component, platform), payload)
component, 'mqtt'), payload)

hass.data[DATA_CONFIG_ENTRY_LOCK] = asyncio.Lock()
hass.data[CONFIG_ENTRY_IS_SETUP] = set()
Expand Down
17 changes: 17 additions & 0 deletions tests/components/light/test_mqtt.py
Original file line number Diff line number Diff line change
Expand Up @@ -1064,3 +1064,20 @@ async def test_discovery_removal_light(hass, mqtt_mock, caplog):

state = hass.states.get('light.beer')
assert state is None


async def test_discovery_deprecated(hass, mqtt_mock, caplog):
"""Test removal of discovered mqtt_json lights."""
entry = MockConfigEntry(domain=mqtt.DOMAIN)
await async_start(hass, 'homeassistant', {'mqtt': {}}, entry)
data = (
'{ "name": "Beer",'
' "platform": "mqtt",'
' "command_topic": "test_topic"}'
)
async_fire_mqtt_message(hass, 'homeassistant/light/bla/config',
data)
await hass.async_block_till_done()
state = hass.states.get('light.beer')
assert state is not None
assert state.name == 'Beer'
17 changes: 17 additions & 0 deletions tests/components/light/test_mqtt_json.py
Original file line number Diff line number Diff line change
Expand Up @@ -554,3 +554,20 @@ async def test_discovery_removal(hass, mqtt_mock, caplog):
await hass.async_block_till_done()
state = hass.states.get('light.beer')
assert state is None


async def test_discovery_deprecated(hass, mqtt_mock, caplog):
"""Test removal of discovered mqtt_json lights."""
entry = MockConfigEntry(domain=mqtt.DOMAIN)
await async_start(hass, 'homeassistant', {'mqtt': {}}, entry)
data = (
'{ "name": "Beer",'
' "platform": "mqtt_json",'
' "command_topic": "test_topic"}'
)
async_fire_mqtt_message(hass, 'homeassistant/light/bla/config',
data)
await hass.async_block_till_done()
state = hass.states.get('light.beer')
assert state is not None
assert state.name == 'Beer'
58 changes: 57 additions & 1 deletion tests/components/light/test_mqtt_template.py
Original file line number Diff line number Diff line change
Expand Up @@ -52,6 +52,41 @@ async def test_setup_fails(hass, mqtt_mock):
})
assert hass.states.get('light.test') is None

with assert_setup_component(0, light.DOMAIN):
assert await async_setup_component(hass, light.DOMAIN, {
light.DOMAIN: {
'platform': 'mqtt',
'schema': 'template',
'name': 'test',
'command_topic': 'test_topic',
}
})
assert hass.states.get('light.test') is None

with assert_setup_component(0, light.DOMAIN):
assert await async_setup_component(hass, light.DOMAIN, {
light.DOMAIN: {
'platform': 'mqtt',
'schema': 'template',
'name': 'test',
'command_topic': 'test_topic',
'command_on_template': 'on',
}
})
assert hass.states.get('light.test') is None

with assert_setup_component(0, light.DOMAIN):
assert await async_setup_component(hass, light.DOMAIN, {
light.DOMAIN: {
'platform': 'mqtt',
'schema': 'template',
'name': 'test',
'command_topic': 'test_topic',
'command_off_template': 'off',
}
})
assert hass.states.get('light.test') is None


async def test_state_change_via_topic(hass, mqtt_mock):
"""Test state change via topic."""
Expand Down Expand Up @@ -456,7 +491,28 @@ async def test_discovery(hass, mqtt_mock, caplog):
data = (
'{ "name": "Beer",'
' "schema": "template",'
' "command_topic": "test_topic" }'
' "command_topic": "test_topic",'
' "command_on_template": "on",'
' "command_off_template": "off"}'
)
async_fire_mqtt_message(hass, 'homeassistant/light/bla/config',
data)
await hass.async_block_till_done()
state = hass.states.get('light.beer')
assert state is not None
assert state.name == 'Beer'


async def test_discovery_deprecated(hass, mqtt_mock, caplog):
"""Test removal of discovered mqtt_json lights."""
entry = MockConfigEntry(domain=mqtt.DOMAIN)
await async_start(hass, 'homeassistant', {'mqtt': {}}, entry)
data = (
'{ "name": "Beer",'
' "platform": "mqtt_template",'
' "command_topic": "test_topic",'
' "command_on_template": "on",'
' "command_off_template": "off"}'
)
async_fire_mqtt_message(hass, 'homeassistant/light/bla/config',
data)
Expand Down

0 comments on commit 4605e14

Please sign in to comment.