Add support for Tasmota status sensor (#41782)

* Add sensor attributes, remove useless test.

* Fix tests

* Rework handling of sensor attributes

* Remove unused sensor attributes

* Hide status sensors

* Bump hatasmota to 0.0.19

* Use DEVICE_CLASS_SIGNAL_STRENGTH for WiFi signal sensor

* Improve test coverage

* Fix tests
This commit is contained in:
Erik Montnemery 2020-10-19 22:07:31 +02:00 committed by GitHub
parent 4da6c22338
commit ec7f329807
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
8 changed files with 90 additions and 37 deletions

View file

@ -25,6 +25,7 @@ _LOGGER = logging.getLogger(__name__)
SUPPORTED_PLATFORMS = [
"binary_sensor",
"light",
"sensor",
"switch",
]

View file

@ -3,7 +3,7 @@
"name": "Tasmota (beta)",
"config_flow": true,
"documentation": "https://www.home-assistant.io/integrations/tasmota",
"requirements": ["hatasmota==0.0.18"],
"requirements": ["hatasmota==0.0.19"],
"dependencies": ["mqtt"],
"mqtt": ["tasmota/discovery/#"],
"codeowners": ["@emontnemery"]

View file

@ -33,6 +33,7 @@ from hatasmota.const import (
SENSOR_PRESSUREATSEALEVEL,
SENSOR_PROXIMITY,
SENSOR_REACTIVE_POWERUSAGE,
SENSOR_STATUS_SIGNAL,
SENSOR_TEMPERATURE,
SENSOR_TODAY,
SENSOR_TOTAL,
@ -50,8 +51,10 @@ from homeassistant.const import (
DEVICE_CLASS_ILLUMINANCE,
DEVICE_CLASS_POWER,
DEVICE_CLASS_PRESSURE,
DEVICE_CLASS_SIGNAL_STRENGTH,
DEVICE_CLASS_TEMPERATURE,
)
from homeassistant.core import callback
from homeassistant.helpers.dispatcher import async_dispatcher_connect
from homeassistant.helpers.entity import Entity
@ -95,6 +98,7 @@ SENSOR_DEVICE_CLASS_ICON_MAP = {
SENSOR_PRESSUREATSEALEVEL: {DEVICE_CLASS: DEVICE_CLASS_PRESSURE},
SENSOR_PROXIMITY: {ICON: "mdi:ruler"},
SENSOR_REACTIVE_POWERUSAGE: {DEVICE_CLASS: DEVICE_CLASS_POWER},
SENSOR_STATUS_SIGNAL: {DEVICE_CLASS: DEVICE_CLASS_SIGNAL_STRENGTH},
SENSOR_TEMPERATURE: {DEVICE_CLASS: DEVICE_CLASS_TEMPERATURE},
SENSOR_TODAY: {DEVICE_CLASS: DEVICE_CLASS_POWER},
SENSOR_TOTAL: {DEVICE_CLASS: DEVICE_CLASS_POWER},
@ -131,13 +135,19 @@ class TasmotaSensor(TasmotaAvailability, TasmotaDiscoveryUpdate, Entity):
def __init__(self, **kwds):
"""Initialize the Tasmota sensor."""
self._state = False
self._state = None
super().__init__(
discovery_update=self.discovery_update,
**kwds,
)
@callback
def state_updated(self, state, **kwargs):
"""Handle state updates."""
self._state = state
self.async_write_ha_state()
@property
def device_class(self) -> Optional[str]:
"""Return the device class of the sensor."""
@ -146,6 +156,14 @@ class TasmotaSensor(TasmotaAvailability, TasmotaDiscoveryUpdate, Entity):
)
return class_or_icon.get(DEVICE_CLASS)
@property
def entity_registry_enabled_default(self) -> bool:
"""Return if the entity should be enabled when first added to the entity registry."""
# Hide status sensors to not overwhelm users
if self._tasmota_entity.quantity == SENSOR_STATUS_SIGNAL:
return False
return True
@property
def icon(self):
"""Return the icon."""

View file

@ -729,7 +729,7 @@ hass-nabucasa==0.37.1
hass_splunk==0.1.1
# homeassistant.components.tasmota
hatasmota==0.0.18
hatasmota==0.0.19
# homeassistant.components.jewish_calendar
hdate==0.9.5

View file

@ -364,7 +364,7 @@ hangups==0.4.11
hass-nabucasa==0.37.1
# homeassistant.components.tasmota
hatasmota==0.0.18
hatasmota==0.0.19
# homeassistant.components.jewish_calendar
hdate==0.9.5

View file

@ -1,5 +1,6 @@
"""Test fixtures for Tasmota component."""
from hatasmota.discovery import get_status_sensor_entities
import pytest
from homeassistant import config_entries
@ -43,6 +44,20 @@ def disable_debounce():
yield
@pytest.fixture
def status_sensor_disabled():
"""Fixture to allow overriding MQTT config."""
return True
@pytest.fixture(autouse=True)
def disable_status_sensor(status_sensor_disabled):
"""Disable Tasmota status sensor."""
wraps = None if status_sensor_disabled else get_status_sensor_entities
with patch("hatasmota.discovery.get_status_sensor_entities", wraps=wraps):
yield
async def setup_tasmota_helper(hass):
"""Set up Tasmota."""
hass.config.components.add("tasmota")

View file

@ -2,8 +2,6 @@
import copy
import json
import pytest
from homeassistant.components.tasmota.const import DEFAULT_PREFIX
from homeassistant.components.tasmota.discovery import ALREADY_DISCOVERED
@ -174,37 +172,6 @@ async def test_device_update(
assert device_entry.sw_version == "v6.6.6"
@pytest.mark.no_fail_on_log_exception
async def test_discovery_broken(hass, mqtt_mock, caplog, device_reg, setup_tasmota):
"""Test handling of exception when creating discovered device."""
config = copy.deepcopy(DEFAULT_CONFIG)
mac = config["mac"]
data = json.dumps(config)
# Trigger an exception when the entity is added
with patch(
"hatasmota.discovery.get_device_config_helper",
return_value=object(),
):
async_fire_mqtt_message(hass, f"{DEFAULT_PREFIX}/{mac}/config", data)
await hass.async_block_till_done()
# Verify device entry is not created
device_entry = device_reg.async_get_device(set(), {("mac", mac)})
assert device_entry is None
assert (
"Exception in async_discover_device when dispatching 'tasmota_discovery_device'"
in caplog.text
)
async_fire_mqtt_message(hass, f"{DEFAULT_PREFIX}/{mac}/config", data)
await hass.async_block_till_done()
# Verify device entry is created
device_entry = device_reg.async_get_device(set(), {("mac", mac)})
assert device_entry is not None
async def test_device_remove(
hass, mqtt_mock, caplog, device_reg, entity_reg, setup_tasmota
):

View file

@ -7,6 +7,7 @@ from hatasmota.utils import (
get_topic_tele_sensor,
get_topic_tele_will,
)
import pytest
from homeassistant.components import sensor
from homeassistant.components.tasmota.const import DEFAULT_PREFIX
@ -217,6 +218,57 @@ async def test_indexed_sensor_state_via_mqtt(hass, mqtt_mock, setup_tasmota):
assert state.state == "7.8"
@pytest.mark.parametrize("status_sensor_disabled", [False])
async def test_status_sensor_state_via_mqtt(hass, mqtt_mock, setup_tasmota):
"""Test state update via MQTT."""
entity_reg = await hass.helpers.entity_registry.async_get_registry()
# Pre-enable the status sensor
entity_reg.async_get_or_create(
sensor.DOMAIN,
"tasmota",
"00000049A3BC_status_sensor_status_sensor_status_signal",
suggested_object_id="tasmota_status",
disabled_by=None,
)
config = copy.deepcopy(DEFAULT_CONFIG)
mac = config["mac"]
async_fire_mqtt_message(
hass,
f"{DEFAULT_PREFIX}/{mac}/config",
json.dumps(config),
)
await hass.async_block_till_done()
await hass.async_block_till_done()
state = hass.states.get("sensor.tasmota_status")
assert state.state == "unavailable"
assert not state.attributes.get(ATTR_ASSUMED_STATE)
async_fire_mqtt_message(hass, "tasmota_49A3BC/tele/LWT", "Online")
state = hass.states.get("sensor.tasmota_status")
assert state.state == STATE_UNKNOWN
assert not state.attributes.get(ATTR_ASSUMED_STATE)
# Test pushed state update
async_fire_mqtt_message(
hass, "tasmota_49A3BC/tele/STATE", '{"Wifi":{"Signal":20.5}}'
)
state = hass.states.get("sensor.tasmota_status")
assert state.state == "20.5"
# Test polled state update
async_fire_mqtt_message(
hass,
"tasmota_49A3BC/stat/STATUS11",
'{"StatusSTS":{"Wifi":{"Signal":20.0}}}',
)
state = hass.states.get("sensor.tasmota_status")
assert state.state == "20.0"
async def test_attributes(hass, mqtt_mock, setup_tasmota):
"""Test correct attributes for sensors."""
config = copy.deepcopy(DEFAULT_CONFIG)