Add Tasmota sensor (#41483)

* Add Tasmota sensor

* Remove useless try-except

* Bump hatasmota to 0.0.11

* Apply suggestions from code review

Co-authored-by: Paulus Schoutsen <paulus@home-assistant.io>

* Sort dict constants

Co-authored-by: Paulus Schoutsen <paulus@home-assistant.io>
This commit is contained in:
Erik Montnemery 2020-10-12 07:27:06 +02:00 committed by GitHub
parent 59edb25e4e
commit 84cb00bb4e
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
7 changed files with 604 additions and 36 deletions

View file

@ -57,7 +57,13 @@ DEFAULT_CONFIG = {
async def help_test_availability_when_connection_lost(
hass, mqtt_client_mock, mqtt_mock, domain, config
hass,
mqtt_client_mock,
mqtt_mock,
domain,
config,
sensor_config=None,
entity_id="test",
):
"""Test availability after MQTT disconnection.
@ -69,6 +75,13 @@ async def help_test_availability_when_connection_lost(
json.dumps(config),
)
await hass.async_block_till_done()
if sensor_config:
async_fire_mqtt_message(
hass,
f"{DEFAULT_PREFIX}/{config[CONF_MAC]}/sensors",
json.dumps(sensor_config),
)
await hass.async_block_till_done()
# Device online
async_fire_mqtt_message(
@ -76,7 +89,8 @@ async def help_test_availability_when_connection_lost(
get_topic_tele_will(config),
config_get_state_online(config),
)
state = hass.states.get(f"{domain}.test")
state = hass.states.get(f"{domain}.{entity_id}")
assert state.state != STATE_UNAVAILABLE
# Disconnected from MQTT server -> state changed to unavailable
@ -85,7 +99,7 @@ async def help_test_availability_when_connection_lost(
await hass.async_block_till_done()
await hass.async_block_till_done()
await hass.async_block_till_done()
state = hass.states.get(f"{domain}.test")
state = hass.states.get(f"{domain}.{entity_id}")
assert state.state == STATE_UNAVAILABLE
# Reconnected to MQTT server -> state still unavailable
@ -94,7 +108,7 @@ async def help_test_availability_when_connection_lost(
await hass.async_block_till_done()
await hass.async_block_till_done()
await hass.async_block_till_done()
state = hass.states.get(f"{domain}.test")
state = hass.states.get(f"{domain}.{entity_id}")
assert state.state == STATE_UNAVAILABLE
# Receive LWT again
@ -103,7 +117,7 @@ async def help_test_availability_when_connection_lost(
get_topic_tele_will(config),
config_get_state_online(config),
)
state = hass.states.get(f"{domain}.test")
state = hass.states.get(f"{domain}.{entity_id}")
assert state.state != STATE_UNAVAILABLE
@ -112,6 +126,8 @@ async def help_test_availability(
mqtt_mock,
domain,
config,
sensor_config=None,
entity_id="test",
):
"""Test availability.
@ -123,8 +139,15 @@ async def help_test_availability(
json.dumps(config),
)
await hass.async_block_till_done()
if sensor_config:
async_fire_mqtt_message(
hass,
f"{DEFAULT_PREFIX}/{config[CONF_MAC]}/sensors",
json.dumps(sensor_config),
)
await hass.async_block_till_done()
state = hass.states.get(f"{domain}.test")
state = hass.states.get(f"{domain}.{entity_id}")
assert state.state == STATE_UNAVAILABLE
async_fire_mqtt_message(
@ -133,7 +156,7 @@ async def help_test_availability(
config_get_state_online(config),
)
state = hass.states.get(f"{domain}.test")
state = hass.states.get(f"{domain}.{entity_id}")
assert state.state != STATE_UNAVAILABLE
async_fire_mqtt_message(
@ -142,7 +165,7 @@ async def help_test_availability(
config_get_state_offline(config),
)
state = hass.states.get(f"{domain}.test")
state = hass.states.get(f"{domain}.{entity_id}")
assert state.state == STATE_UNAVAILABLE
@ -151,6 +174,8 @@ async def help_test_availability_discovery_update(
mqtt_mock,
domain,
config,
sensor_config=None,
entity_id="test",
):
"""Test update of discovered TasmotaAvailability.
@ -180,16 +205,23 @@ async def help_test_availability_discovery_update(
async_fire_mqtt_message(hass, f"{DEFAULT_PREFIX}/{config1[CONF_MAC]}/config", data1)
await hass.async_block_till_done()
if sensor_config:
async_fire_mqtt_message(
hass,
f"{DEFAULT_PREFIX}/{config[CONF_MAC]}/sensors",
json.dumps(sensor_config),
)
await hass.async_block_till_done()
state = hass.states.get(f"{domain}.test")
state = hass.states.get(f"{domain}.{entity_id}")
assert state.state == STATE_UNAVAILABLE
async_fire_mqtt_message(hass, availability_topic1, online1)
state = hass.states.get(f"{domain}.test")
state = hass.states.get(f"{domain}.{entity_id}")
assert state.state != STATE_UNAVAILABLE
async_fire_mqtt_message(hass, availability_topic1, offline1)
state = hass.states.get(f"{domain}.test")
state = hass.states.get(f"{domain}.{entity_id}")
assert state.state == STATE_UNAVAILABLE
# Change availability settings
@ -200,17 +232,24 @@ async def help_test_availability_discovery_update(
async_fire_mqtt_message(hass, availability_topic1, online1)
async_fire_mqtt_message(hass, availability_topic1, online2)
async_fire_mqtt_message(hass, availability_topic2, online1)
state = hass.states.get(f"{domain}.test")
state = hass.states.get(f"{domain}.{entity_id}")
assert state.state == STATE_UNAVAILABLE
# Verify we are subscribing to the new topic
async_fire_mqtt_message(hass, availability_topic2, online2)
state = hass.states.get(f"{domain}.test")
state = hass.states.get(f"{domain}.{entity_id}")
assert state.state != STATE_UNAVAILABLE
async def help_test_availability_poll_state(
hass, mqtt_client_mock, mqtt_mock, domain, config, poll_topic, poll_payload
hass,
mqtt_client_mock,
mqtt_mock,
domain,
config,
poll_topic,
poll_payload,
sensor_config=None,
):
"""Test polling of state when device is available.
@ -222,6 +261,13 @@ async def help_test_availability_poll_state(
json.dumps(config),
)
await hass.async_block_till_done()
if sensor_config:
async_fire_mqtt_message(
hass,
f"{DEFAULT_PREFIX}/{config[CONF_MAC]}/sensors",
json.dumps(sensor_config),
)
await hass.async_block_till_done()
mqtt_mock.async_publish.reset_mock()
# Device online, verify poll for state
@ -265,7 +311,16 @@ async def help_test_availability_poll_state(
async def help_test_discovery_removal(
hass, mqtt_mock, caplog, domain, config1, config2
hass,
mqtt_mock,
caplog,
domain,
config1,
config2,
sensor_config1=None,
sensor_config2=None,
entity_id="test",
name="Test",
):
"""Test removal of discovered entity."""
device_reg = await hass.helpers.device_registry.async_get_registry()
@ -277,34 +332,56 @@ async def help_test_discovery_removal(
async_fire_mqtt_message(hass, f"{DEFAULT_PREFIX}/{config1[CONF_MAC]}/config", data1)
await hass.async_block_till_done()
if sensor_config1:
async_fire_mqtt_message(
hass,
f"{DEFAULT_PREFIX}/{config1[CONF_MAC]}/sensors",
json.dumps(sensor_config1),
)
await hass.async_block_till_done()
# Verify device and entity registry entries are created
device_entry = device_reg.async_get_device(set(), {("mac", config1[CONF_MAC])})
assert device_entry is not None
entity_entry = entity_reg.async_get(f"{domain}.test")
entity_entry = entity_reg.async_get(f"{domain}.{entity_id}")
assert entity_entry is not None
# Verify state is added
state = hass.states.get(f"{domain}.test")
state = hass.states.get(f"{domain}.{entity_id}")
assert state is not None
assert state.name == "Test"
assert state.name == name
async_fire_mqtt_message(hass, f"{DEFAULT_PREFIX}/{config2[CONF_MAC]}/config", data2)
await hass.async_block_till_done()
if sensor_config1:
async_fire_mqtt_message(
hass,
f"{DEFAULT_PREFIX}/{config2[CONF_MAC]}/sensors",
json.dumps(sensor_config2),
)
await hass.async_block_till_done()
# Verify entity registry entries are cleared
device_entry = device_reg.async_get_device(set(), {("mac", config2[CONF_MAC])})
assert device_entry is not None
entity_entry = entity_reg.async_get(f"{domain}.test")
entity_entry = entity_reg.async_get(f"{domain}.{entity_id}")
assert entity_entry is None
# Verify state is removed
state = hass.states.get(f"{domain}.test")
state = hass.states.get(f"{domain}.{entity_id}")
assert state is None
async def help_test_discovery_update_unchanged(
hass, mqtt_mock, caplog, domain, config, discovery_update
hass,
mqtt_mock,
caplog,
domain,
config,
discovery_update,
sensor_config=None,
entity_id="test",
name="Test",
):
"""Test update of discovered component without changes.
@ -313,18 +390,33 @@ async def help_test_discovery_update_unchanged(
config1 = copy.deepcopy(config)
config2 = copy.deepcopy(config)
config2[CONF_PREFIX][PREFIX_CMND] = "cmnd2"
config2[CONF_PREFIX][PREFIX_TELE] = "tele2"
data1 = json.dumps(config1)
data2 = json.dumps(config2)
async_fire_mqtt_message(hass, f"{DEFAULT_PREFIX}/{config[CONF_MAC]}/config", data1)
await hass.async_block_till_done()
if sensor_config:
async_fire_mqtt_message(
hass,
f"{DEFAULT_PREFIX}/{config[CONF_MAC]}/sensors",
json.dumps(sensor_config),
)
await hass.async_block_till_done()
state = hass.states.get(f"{domain}.test")
state = hass.states.get(f"{domain}.{entity_id}")
assert state is not None
assert state.name == "Test"
assert state.name == name
async_fire_mqtt_message(hass, f"{DEFAULT_PREFIX}/{config[CONF_MAC]}/config", data1)
await hass.async_block_till_done()
if sensor_config:
async_fire_mqtt_message(
hass,
f"{DEFAULT_PREFIX}/{config[CONF_MAC]}/sensors",
json.dumps(sensor_config),
)
await hass.async_block_till_done()
assert not discovery_update.called
@ -334,7 +426,9 @@ async def help_test_discovery_update_unchanged(
assert discovery_update.called
async def help_test_discovery_device_remove(hass, mqtt_mock, domain, unique_id, config):
async def help_test_discovery_device_remove(
hass, mqtt_mock, domain, unique_id, config, sensor_config=None
):
"""Test domain entity is removed when device is removed."""
device_reg = await hass.helpers.device_registry.async_get_registry()
entity_reg = await hass.helpers.entity_registry.async_get_registry()
@ -344,6 +438,13 @@ async def help_test_discovery_device_remove(hass, mqtt_mock, domain, unique_id,
data = json.dumps(config)
async_fire_mqtt_message(hass, f"{DEFAULT_PREFIX}/{config[CONF_MAC]}/config", data)
await hass.async_block_till_done()
if sensor_config:
async_fire_mqtt_message(
hass,
f"{DEFAULT_PREFIX}/{config[CONF_MAC]}/sensors",
json.dumps(sensor_config),
)
await hass.async_block_till_done()
device = device_reg.async_get_device(set(), {("mac", config[CONF_MAC])})
assert device is not None
@ -358,7 +459,7 @@ async def help_test_discovery_device_remove(hass, mqtt_mock, domain, unique_id,
async def help_test_entity_id_update_subscriptions(
hass, mqtt_mock, domain, config, topics=None
hass, mqtt_mock, domain, config, topics=None, sensor_config=None, entity_id="test"
):
"""Test MQTT subscriptions are managed when entity_id is updated."""
entity_reg = await hass.helpers.entity_registry.async_get_registry()
@ -370,22 +471,31 @@ async def help_test_entity_id_update_subscriptions(
async_fire_mqtt_message(hass, f"{DEFAULT_PREFIX}/{config[CONF_MAC]}/config", data)
await hass.async_block_till_done()
if sensor_config:
async_fire_mqtt_message(
hass,
f"{DEFAULT_PREFIX}/{config[CONF_MAC]}/sensors",
json.dumps(sensor_config),
)
await hass.async_block_till_done()
if not topics:
topics = [get_topic_tele_state(config), get_topic_tele_will(config)]
assert len(topics) > 0
state = hass.states.get(f"{domain}.test")
state = hass.states.get(f"{domain}.{entity_id}")
assert state is not None
assert mqtt_mock.async_subscribe.call_count == len(topics)
for topic in topics:
mqtt_mock.async_subscribe.assert_any_call(topic, ANY, ANY, ANY)
mqtt_mock.async_subscribe.reset_mock()
entity_reg.async_update_entity(f"{domain}.test", new_entity_id=f"{domain}.milk")
entity_reg.async_update_entity(
f"{domain}.{entity_id}", new_entity_id=f"{domain}.milk"
)
await hass.async_block_till_done()
state = hass.states.get(f"{domain}.test")
state = hass.states.get(f"{domain}.{entity_id}")
assert state is None
state = hass.states.get(f"{domain}.milk")
@ -394,7 +504,9 @@ async def help_test_entity_id_update_subscriptions(
mqtt_mock.async_subscribe.assert_any_call(topic, ANY, ANY, ANY)
async def help_test_entity_id_update_discovery_update(hass, mqtt_mock, domain, config):
async def help_test_entity_id_update_discovery_update(
hass, mqtt_mock, domain, config, sensor_config=None, entity_id="test"
):
"""Test MQTT discovery update after entity_id is updated."""
entity_reg = await hass.helpers.entity_registry.async_get_registry()
@ -405,16 +517,25 @@ async def help_test_entity_id_update_discovery_update(hass, mqtt_mock, domain, c
async_fire_mqtt_message(hass, f"{DEFAULT_PREFIX}/{config[CONF_MAC]}/config", data)
await hass.async_block_till_done()
if sensor_config:
async_fire_mqtt_message(
hass,
f"{DEFAULT_PREFIX}/{config[CONF_MAC]}/sensors",
json.dumps(sensor_config),
)
await hass.async_block_till_done()
async_fire_mqtt_message(hass, topic, config_get_state_online(config))
state = hass.states.get(f"{domain}.test")
state = hass.states.get(f"{domain}.{entity_id}")
assert state.state != STATE_UNAVAILABLE
async_fire_mqtt_message(hass, topic, config_get_state_offline(config))
state = hass.states.get(f"{domain}.test")
state = hass.states.get(f"{domain}.{entity_id}")
assert state.state == STATE_UNAVAILABLE
entity_reg.async_update_entity(f"{domain}.test", new_entity_id=f"{domain}.milk")
entity_reg.async_update_entity(
f"{domain}.{entity_id}", new_entity_id=f"{domain}.milk"
)
await hass.async_block_till_done()
assert hass.states.get(f"{domain}.milk")