Allow None device_class and UOM for mqtt entities (#91240)
* Allow None device_class and UOM for mqtt entities * Rever not needed changes * Revert another unwanted change
This commit is contained in:
parent
325733158d
commit
e36fd5f222
10 changed files with 190 additions and 76 deletions
|
@ -125,7 +125,7 @@ _PLATFORM_SCHEMA_BASE = MQTT_RW_SCHEMA.extend(
|
||||||
vol.Optional(
|
vol.Optional(
|
||||||
CONF_DEVICE_CLASS, default=HumidifierDeviceClass.HUMIDIFIER
|
CONF_DEVICE_CLASS, default=HumidifierDeviceClass.HUMIDIFIER
|
||||||
): vol.In(
|
): vol.In(
|
||||||
[HumidifierDeviceClass.HUMIDIFIER, HumidifierDeviceClass.DEHUMIDIFIER]
|
[HumidifierDeviceClass.HUMIDIFIER, HumidifierDeviceClass.DEHUMIDIFIER, None]
|
||||||
),
|
),
|
||||||
vol.Optional(CONF_MODE_COMMAND_TEMPLATE): cv.template,
|
vol.Optional(CONF_MODE_COMMAND_TEMPLATE): cv.template,
|
||||||
vol.Optional(CONF_MODE_STATE_TOPIC): valid_subscribe_topic,
|
vol.Optional(CONF_MODE_STATE_TOPIC): valid_subscribe_topic,
|
||||||
|
|
|
@ -97,7 +97,7 @@ _PLATFORM_SCHEMA_BASE = MQTT_RW_SCHEMA.extend(
|
||||||
vol.Optional(CONF_STEP, default=DEFAULT_STEP): vol.All(
|
vol.Optional(CONF_STEP, default=DEFAULT_STEP): vol.All(
|
||||||
vol.Coerce(float), vol.Range(min=1e-3)
|
vol.Coerce(float), vol.Range(min=1e-3)
|
||||||
),
|
),
|
||||||
vol.Optional(CONF_UNIT_OF_MEASUREMENT): cv.string,
|
vol.Optional(CONF_UNIT_OF_MEASUREMENT): vol.Any(cv.string, None),
|
||||||
vol.Optional(CONF_VALUE_TEMPLATE): cv.template,
|
vol.Optional(CONF_VALUE_TEMPLATE): cv.template,
|
||||||
},
|
},
|
||||||
).extend(MQTT_ENTITY_COMMON_SCHEMA.schema)
|
).extend(MQTT_ENTITY_COMMON_SCHEMA.schema)
|
||||||
|
|
|
@ -107,7 +107,7 @@ _PLATFORM_SCHEMA_BASE = MQTT_RO_SCHEMA.extend(
|
||||||
vol.Optional(CONF_NAME, default=DEFAULT_NAME): cv.string,
|
vol.Optional(CONF_NAME, default=DEFAULT_NAME): cv.string,
|
||||||
vol.Optional(CONF_SUGGESTED_DISPLAY_PRECISION): cv.positive_int,
|
vol.Optional(CONF_SUGGESTED_DISPLAY_PRECISION): cv.positive_int,
|
||||||
vol.Optional(CONF_STATE_CLASS): vol.Any(STATE_CLASSES_SCHEMA, None),
|
vol.Optional(CONF_STATE_CLASS): vol.Any(STATE_CLASSES_SCHEMA, None),
|
||||||
vol.Optional(CONF_UNIT_OF_MEASUREMENT): cv.string,
|
vol.Optional(CONF_UNIT_OF_MEASUREMENT): vol.Any(cv.string, None),
|
||||||
}
|
}
|
||||||
).extend(MQTT_ENTITY_COMMON_SCHEMA.schema)
|
).extend(MQTT_ENTITY_COMMON_SCHEMA.schema)
|
||||||
|
|
||||||
|
|
|
@ -505,8 +505,9 @@ async def test_setting_sensor_value_via_mqtt_message_empty_template(
|
||||||
|
|
||||||
|
|
||||||
@pytest.mark.parametrize(
|
@pytest.mark.parametrize(
|
||||||
"hass_config",
|
("hass_config", "device_class"),
|
||||||
[
|
[
|
||||||
|
(
|
||||||
{
|
{
|
||||||
mqtt.DOMAIN: {
|
mqtt.DOMAIN: {
|
||||||
binary_sensor.DOMAIN: {
|
binary_sensor.DOMAIN: {
|
||||||
|
@ -515,17 +516,33 @@ async def test_setting_sensor_value_via_mqtt_message_empty_template(
|
||||||
"state_topic": "test-topic",
|
"state_topic": "test-topic",
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
},
|
||||||
|
"motion",
|
||||||
|
),
|
||||||
|
(
|
||||||
|
{
|
||||||
|
mqtt.DOMAIN: {
|
||||||
|
binary_sensor.DOMAIN: {
|
||||||
|
"name": "test",
|
||||||
|
"device_class": None,
|
||||||
|
"state_topic": "test-topic",
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
None,
|
||||||
|
),
|
||||||
],
|
],
|
||||||
)
|
)
|
||||||
async def test_valid_device_class(
|
async def test_valid_device_class(
|
||||||
hass: HomeAssistant, mqtt_mock_entry: MqttMockHAClientGenerator
|
hass: HomeAssistant,
|
||||||
|
mqtt_mock_entry: MqttMockHAClientGenerator,
|
||||||
|
device_class: str | None,
|
||||||
) -> None:
|
) -> None:
|
||||||
"""Test the setting of a valid sensor class."""
|
"""Test the setting of a valid sensor class and ignoring an empty device_class."""
|
||||||
await mqtt_mock_entry()
|
await mqtt_mock_entry()
|
||||||
|
|
||||||
state = hass.states.get("binary_sensor.test")
|
state = hass.states.get("binary_sensor.test")
|
||||||
assert state.attributes.get("device_class") == "motion"
|
assert state.attributes.get("device_class") == device_class
|
||||||
|
|
||||||
|
|
||||||
@pytest.mark.parametrize(
|
@pytest.mark.parametrize(
|
||||||
|
|
|
@ -477,6 +477,11 @@ async def test_invalid_device_class(
|
||||||
"name": "Test 3",
|
"name": "Test 3",
|
||||||
"command_topic": "test-topic",
|
"command_topic": "test-topic",
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
"name": "Test 4",
|
||||||
|
"command_topic": "test-topic",
|
||||||
|
"device_class": None,
|
||||||
|
},
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -867,6 +867,19 @@ async def test_attributes(
|
||||||
},
|
},
|
||||||
True,
|
True,
|
||||||
),
|
),
|
||||||
|
(
|
||||||
|
{
|
||||||
|
mqtt.DOMAIN: {
|
||||||
|
humidifier.DOMAIN: {
|
||||||
|
"name": "test_valid_4",
|
||||||
|
"command_topic": "command-topic",
|
||||||
|
"target_humidity_command_topic": "humidity-command-topic",
|
||||||
|
"device_class": None,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
True,
|
||||||
|
),
|
||||||
(
|
(
|
||||||
{
|
{
|
||||||
mqtt.DOMAIN: {
|
mqtt.DOMAIN: {
|
||||||
|
|
|
@ -18,7 +18,6 @@ from homeassistant.components.number import (
|
||||||
ATTR_VALUE,
|
ATTR_VALUE,
|
||||||
DOMAIN as NUMBER_DOMAIN,
|
DOMAIN as NUMBER_DOMAIN,
|
||||||
SERVICE_SET_VALUE,
|
SERVICE_SET_VALUE,
|
||||||
NumberDeviceClass,
|
|
||||||
)
|
)
|
||||||
from homeassistant.const import (
|
from homeassistant.const import (
|
||||||
ATTR_ASSUMED_STATE,
|
ATTR_ASSUMED_STATE,
|
||||||
|
@ -76,8 +75,9 @@ def number_platform_only():
|
||||||
|
|
||||||
|
|
||||||
@pytest.mark.parametrize(
|
@pytest.mark.parametrize(
|
||||||
"hass_config",
|
("hass_config", "device_class", "unit_of_measurement", "values"),
|
||||||
[
|
[
|
||||||
|
(
|
||||||
{
|
{
|
||||||
mqtt.DOMAIN: {
|
mqtt.DOMAIN: {
|
||||||
number.DOMAIN: {
|
number.DOMAIN: {
|
||||||
|
@ -89,32 +89,66 @@ def number_platform_only():
|
||||||
"payload_reset": "reset!",
|
"payload_reset": "reset!",
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
},
|
||||||
|
"temperature",
|
||||||
|
UnitOfTemperature.CELSIUS.value,
|
||||||
|
[("10", "-12.0"), ("20.5", "-6.4")], # 10 °F -> -12 °C
|
||||||
|
),
|
||||||
|
(
|
||||||
|
{
|
||||||
|
mqtt.DOMAIN: {
|
||||||
|
number.DOMAIN: {
|
||||||
|
"state_topic": "test/state_number",
|
||||||
|
"command_topic": "test/cmd_number",
|
||||||
|
"name": "Test Number",
|
||||||
|
"device_class": "temperature",
|
||||||
|
"unit_of_measurement": UnitOfTemperature.CELSIUS.value,
|
||||||
|
"payload_reset": "reset!",
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"temperature",
|
||||||
|
UnitOfTemperature.CELSIUS.value,
|
||||||
|
[("10", "10"), ("15", "15")],
|
||||||
|
),
|
||||||
|
(
|
||||||
|
{
|
||||||
|
mqtt.DOMAIN: {
|
||||||
|
number.DOMAIN: {
|
||||||
|
"state_topic": "test/state_number",
|
||||||
|
"command_topic": "test/cmd_number",
|
||||||
|
"name": "Test Number",
|
||||||
|
"device_class": None,
|
||||||
|
"unit_of_measurement": None,
|
||||||
|
"payload_reset": "reset!",
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
None,
|
||||||
|
None,
|
||||||
|
[("10", "10"), ("20", "20")],
|
||||||
|
),
|
||||||
],
|
],
|
||||||
)
|
)
|
||||||
async def test_run_number_setup(
|
async def test_run_number_setup(
|
||||||
hass: HomeAssistant, mqtt_mock_entry: MqttMockHAClientGenerator
|
hass: HomeAssistant,
|
||||||
|
mqtt_mock_entry: MqttMockHAClientGenerator,
|
||||||
|
device_class: str | None,
|
||||||
|
unit_of_measurement: UnitOfTemperature | None,
|
||||||
|
values: list[tuple[str, str]],
|
||||||
) -> None:
|
) -> None:
|
||||||
"""Test that it fetches the given payload."""
|
"""Test that it fetches the given payload."""
|
||||||
await mqtt_mock_entry()
|
await mqtt_mock_entry()
|
||||||
|
|
||||||
async_fire_mqtt_message(hass, "test/state_number", "10")
|
for payload, value in values:
|
||||||
|
async_fire_mqtt_message(hass, "test/state_number", payload)
|
||||||
|
|
||||||
await hass.async_block_till_done()
|
await hass.async_block_till_done()
|
||||||
|
|
||||||
state = hass.states.get("number.test_number")
|
state = hass.states.get("number.test_number")
|
||||||
assert state.state == "-12.0" # 10 °F -> -12 °C
|
assert state.state == value
|
||||||
assert state.attributes.get(ATTR_DEVICE_CLASS) == NumberDeviceClass.TEMPERATURE
|
assert state.attributes.get(ATTR_DEVICE_CLASS) == device_class
|
||||||
assert state.attributes.get(ATTR_UNIT_OF_MEASUREMENT) == "°C"
|
assert state.attributes.get(ATTR_UNIT_OF_MEASUREMENT) == unit_of_measurement
|
||||||
|
|
||||||
async_fire_mqtt_message(hass, "test/state_number", "20.5")
|
|
||||||
|
|
||||||
await hass.async_block_till_done()
|
|
||||||
|
|
||||||
state = hass.states.get("number.test_number")
|
|
||||||
assert state.state == "-6.4" # 20.5 °F -> -6.4 °C
|
|
||||||
assert state.attributes.get(ATTR_DEVICE_CLASS) == NumberDeviceClass.TEMPERATURE
|
|
||||||
assert state.attributes.get(ATTR_UNIT_OF_MEASUREMENT) == "°C"
|
|
||||||
|
|
||||||
async_fire_mqtt_message(hass, "test/state_number", "reset!")
|
async_fire_mqtt_message(hass, "test/state_number", "reset!")
|
||||||
|
|
||||||
|
@ -122,8 +156,8 @@ async def test_run_number_setup(
|
||||||
|
|
||||||
state = hass.states.get("number.test_number")
|
state = hass.states.get("number.test_number")
|
||||||
assert state.state == "unknown"
|
assert state.state == "unknown"
|
||||||
assert state.attributes.get(ATTR_DEVICE_CLASS) == NumberDeviceClass.TEMPERATURE
|
assert state.attributes.get(ATTR_DEVICE_CLASS) == device_class
|
||||||
assert state.attributes.get(ATTR_UNIT_OF_MEASUREMENT) == "°C"
|
assert state.attributes.get(ATTR_UNIT_OF_MEASUREMENT) == unit_of_measurement
|
||||||
|
|
||||||
|
|
||||||
@pytest.mark.parametrize(
|
@pytest.mark.parametrize(
|
||||||
|
|
|
@ -851,16 +851,17 @@ async def test_invalid_device_class(
|
||||||
"name": "Test 3",
|
"name": "Test 3",
|
||||||
"state_topic": "test-topic",
|
"state_topic": "test-topic",
|
||||||
"device_class": None,
|
"device_class": None,
|
||||||
|
"unit_of_measurement": None,
|
||||||
},
|
},
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
)
|
)
|
||||||
async def test_valid_device_class(
|
async def test_valid_device_class_and_uom(
|
||||||
hass: HomeAssistant, mqtt_mock_entry: MqttMockHAClientGenerator
|
hass: HomeAssistant, mqtt_mock_entry: MqttMockHAClientGenerator
|
||||||
) -> None:
|
) -> None:
|
||||||
"""Test device_class option with valid values."""
|
"""Test device_class option with valid values and test with an empty unit of measurement."""
|
||||||
await mqtt_mock_entry()
|
await mqtt_mock_entry()
|
||||||
|
|
||||||
state = hass.states.get("sensor.test_1")
|
state = hass.states.get("sensor.test_1")
|
||||||
|
|
|
@ -62,8 +62,9 @@ def switch_platform_only():
|
||||||
|
|
||||||
|
|
||||||
@pytest.mark.parametrize(
|
@pytest.mark.parametrize(
|
||||||
"hass_config",
|
("hass_config", "device_class"),
|
||||||
[
|
[
|
||||||
|
(
|
||||||
{
|
{
|
||||||
mqtt.DOMAIN: {
|
mqtt.DOMAIN: {
|
||||||
switch.DOMAIN: {
|
switch.DOMAIN: {
|
||||||
|
@ -75,18 +76,37 @@ def switch_platform_only():
|
||||||
"device_class": "switch",
|
"device_class": "switch",
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
},
|
||||||
|
"switch",
|
||||||
|
),
|
||||||
|
(
|
||||||
|
{
|
||||||
|
mqtt.DOMAIN: {
|
||||||
|
switch.DOMAIN: {
|
||||||
|
"name": "test",
|
||||||
|
"state_topic": "state-topic",
|
||||||
|
"command_topic": "command-topic",
|
||||||
|
"payload_on": 1,
|
||||||
|
"payload_off": 0,
|
||||||
|
"device_class": None,
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
None,
|
||||||
|
),
|
||||||
],
|
],
|
||||||
)
|
)
|
||||||
async def test_controlling_state_via_topic(
|
async def test_controlling_state_via_topic(
|
||||||
hass: HomeAssistant, mqtt_mock_entry: MqttMockHAClientGenerator
|
hass: HomeAssistant,
|
||||||
|
mqtt_mock_entry: MqttMockHAClientGenerator,
|
||||||
|
device_class: str | None,
|
||||||
) -> None:
|
) -> None:
|
||||||
"""Test the controlling state via topic."""
|
"""Test the controlling state via topic."""
|
||||||
await mqtt_mock_entry()
|
await mqtt_mock_entry()
|
||||||
|
|
||||||
state = hass.states.get("switch.test")
|
state = hass.states.get("switch.test")
|
||||||
assert state.state == STATE_UNKNOWN
|
assert state.state == STATE_UNKNOWN
|
||||||
assert state.attributes.get(ATTR_DEVICE_CLASS) == "switch"
|
assert state.attributes.get(ATTR_DEVICE_CLASS) == device_class
|
||||||
assert not state.attributes.get(ATTR_ASSUMED_STATE)
|
assert not state.attributes.get(ATTR_ASSUMED_STATE)
|
||||||
|
|
||||||
async_fire_mqtt_message(hass, "state-topic", "1")
|
async_fire_mqtt_message(hass, "state-topic", "1")
|
||||||
|
|
|
@ -63,8 +63,9 @@ def update_platform_only():
|
||||||
|
|
||||||
|
|
||||||
@pytest.mark.parametrize(
|
@pytest.mark.parametrize(
|
||||||
"hass_config",
|
("hass_config", "device_class"),
|
||||||
[
|
[
|
||||||
|
(
|
||||||
{
|
{
|
||||||
mqtt.DOMAIN: {
|
mqtt.DOMAIN: {
|
||||||
update.DOMAIN: {
|
update.DOMAIN: {
|
||||||
|
@ -75,13 +76,35 @@ def update_platform_only():
|
||||||
"release_url": "https://example.com/release",
|
"release_url": "https://example.com/release",
|
||||||
"title": "Test Update Title",
|
"title": "Test Update Title",
|
||||||
"entity_picture": "https://example.com/icon.png",
|
"entity_picture": "https://example.com/icon.png",
|
||||||
|
"device_class": "firmware",
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
},
|
||||||
|
"firmware",
|
||||||
|
),
|
||||||
|
(
|
||||||
|
{
|
||||||
|
mqtt.DOMAIN: {
|
||||||
|
update.DOMAIN: {
|
||||||
|
"state_topic": "test/installed-version",
|
||||||
|
"latest_version_topic": "test/latest-version",
|
||||||
|
"name": "Test Update",
|
||||||
|
"release_summary": "Test release summary",
|
||||||
|
"release_url": "https://example.com/release",
|
||||||
|
"title": "Test Update Title",
|
||||||
|
"entity_picture": "https://example.com/icon.png",
|
||||||
|
"device_class": None,
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
None,
|
||||||
|
),
|
||||||
],
|
],
|
||||||
)
|
)
|
||||||
async def test_run_update_setup(
|
async def test_run_update_setup(
|
||||||
hass: HomeAssistant, mqtt_mock_entry: MqttMockHAClientGenerator
|
hass: HomeAssistant,
|
||||||
|
mqtt_mock_entry: MqttMockHAClientGenerator,
|
||||||
|
device_class: str | None,
|
||||||
) -> None:
|
) -> None:
|
||||||
"""Test that it fetches the given payload."""
|
"""Test that it fetches the given payload."""
|
||||||
installed_version_topic = "test/installed-version"
|
installed_version_topic = "test/installed-version"
|
||||||
|
@ -101,6 +124,7 @@ async def test_run_update_setup(
|
||||||
assert state.attributes.get("release_url") == "https://example.com/release"
|
assert state.attributes.get("release_url") == "https://example.com/release"
|
||||||
assert state.attributes.get("title") == "Test Update Title"
|
assert state.attributes.get("title") == "Test Update Title"
|
||||||
assert state.attributes.get("entity_picture") == "https://example.com/icon.png"
|
assert state.attributes.get("entity_picture") == "https://example.com/icon.png"
|
||||||
|
assert state.attributes.get("device_class") == device_class
|
||||||
|
|
||||||
async_fire_mqtt_message(hass, latest_version_topic, "2.0.0")
|
async_fire_mqtt_message(hass, latest_version_topic, "2.0.0")
|
||||||
|
|
||||||
|
|
Loading…
Add table
Reference in a new issue