diff --git a/homeassistant/components/amcrest/__init__.py b/homeassistant/components/amcrest/__init__.py index f6ddc210415..8d274f12044 100644 --- a/homeassistant/components/amcrest/__init__.py +++ b/homeassistant/components/amcrest/__init__.py @@ -201,11 +201,15 @@ def _monitor_events(hass, name, api, event_codes): while True: api.available_flag.wait() try: - for code, start in api.event_actions("All", retries=5): - event_data = {"camera": name, "event": code, "payload": start} + for code, payload in api.event_actions("All", retries=5): + event_data = {"camera": name, "event": code, "payload": payload} hass.bus.fire("amcrest", event_data) if code in event_codes: signal = service_signal(SERVICE_EVENT, name, code) + start = any( + str(key).lower() == "action" and str(val).lower() == "start" + for key, val in payload.items() + ) _LOGGER.debug("Sending signal: '%s': %s", signal, start) dispatcher_send(hass, signal, start) except AmcrestError as error: diff --git a/homeassistant/components/androidtv/manifest.json b/homeassistant/components/androidtv/manifest.json index b86a6d9e40a..9ab02fec68a 100644 --- a/homeassistant/components/androidtv/manifest.json +++ b/homeassistant/components/androidtv/manifest.json @@ -4,7 +4,7 @@ "documentation": "https://www.home-assistant.io/integrations/androidtv", "requirements": [ "adb-shell[async]==0.3.1", - "androidtv[async]==0.0.58", + "androidtv[async]==0.0.59", "pure-python-adb[async]==0.3.0.dev0" ], "codeowners": ["@JeffLIrion"], diff --git a/homeassistant/components/denonavr/manifest.json b/homeassistant/components/denonavr/manifest.json index 123eac5d2bf..ed6b94e207a 100644 --- a/homeassistant/components/denonavr/manifest.json +++ b/homeassistant/components/denonavr/manifest.json @@ -3,7 +3,7 @@ "name": "Denon AVR Network Receivers", "config_flow": true, "documentation": "https://www.home-assistant.io/integrations/denonavr", - "requirements": ["denonavr==0.10.6"], + "requirements": ["denonavr==0.10.7"], "codeowners": ["@scarface-4711", "@starkillerOG"], "ssdp": [ { diff --git a/homeassistant/components/esphome/sensor.py b/homeassistant/components/esphome/sensor.py index ceb391f6bda..12319be8c40 100644 --- a/homeassistant/components/esphome/sensor.py +++ b/homeassistant/components/esphome/sensor.py @@ -6,10 +6,15 @@ import math from aioesphomeapi import SensorInfo, SensorState, TextSensorInfo, TextSensorState import voluptuous as vol -from homeassistant.components.sensor import DEVICE_CLASSES, SensorEntity +from homeassistant.components.sensor import ( + DEVICE_CLASS_TIMESTAMP, + DEVICE_CLASSES, + SensorEntity, +) from homeassistant.config_entries import ConfigEntry from homeassistant.core import HomeAssistant import homeassistant.helpers.config_validation as cv +from homeassistant.util import dt from . import EsphomeEntity, esphome_state_property, platform_async_setup_entry @@ -74,6 +79,8 @@ class EsphomeSensor(EsphomeEntity, SensorEntity): return None if self._state.missing_state: return None + if self.device_class == DEVICE_CLASS_TIMESTAMP: + return dt.utc_from_timestamp(self._state.state).isoformat() return f"{self._state.state:.{self._static_info.accuracy_decimals}f}" @property diff --git a/homeassistant/components/huawei_lte/__init__.py b/homeassistant/components/huawei_lte/__init__.py index 690d9ed63a4..70fb051ce30 100644 --- a/homeassistant/components/huawei_lte/__init__.py +++ b/homeassistant/components/huawei_lte/__init__.py @@ -393,26 +393,29 @@ async def async_setup_entry(hass: HomeAssistant, config_entry: ConfigEntry) -> b router.subscriptions.clear() # Set up device registry - device_data = {} - sw_version = None - if router.data.get(KEY_DEVICE_INFORMATION): - device_info = router.data[KEY_DEVICE_INFORMATION] - sw_version = device_info.get("SoftwareVersion") - if device_info.get("DeviceName"): - device_data["model"] = device_info["DeviceName"] - if not sw_version and router.data.get(KEY_DEVICE_BASIC_INFORMATION): - sw_version = router.data[KEY_DEVICE_BASIC_INFORMATION].get("SoftwareVersion") - if sw_version: - device_data["sw_version"] = sw_version - device_registry = await dr.async_get_registry(hass) - device_registry.async_get_or_create( - config_entry_id=config_entry.entry_id, - connections=router.device_connections, - identifiers=router.device_identifiers, - name=router.device_name, - manufacturer="Huawei", - **device_data, - ) + if router.device_identifiers or router.device_connections: + device_data = {} + sw_version = None + if router.data.get(KEY_DEVICE_INFORMATION): + device_info = router.data[KEY_DEVICE_INFORMATION] + sw_version = device_info.get("SoftwareVersion") + if device_info.get("DeviceName"): + device_data["model"] = device_info["DeviceName"] + if not sw_version and router.data.get(KEY_DEVICE_BASIC_INFORMATION): + sw_version = router.data[KEY_DEVICE_BASIC_INFORMATION].get( + "SoftwareVersion" + ) + if sw_version: + device_data["sw_version"] = sw_version + device_registry = await dr.async_get_registry(hass) + device_registry.async_get_or_create( + config_entry_id=config_entry.entry_id, + connections=router.device_connections, + identifiers=router.device_identifiers, + name=router.device_name, + manufacturer="Huawei", + **device_data, + ) # Forward config entry setup to platforms hass.config_entries.async_setup_platforms(config_entry, CONFIG_ENTRY_PLATFORMS) diff --git a/homeassistant/components/modbus/modbus.py b/homeassistant/components/modbus/modbus.py index f04c019e6a6..6568f552fa6 100644 --- a/homeassistant/components/modbus/modbus.py +++ b/homeassistant/components/modbus/modbus.py @@ -5,7 +5,6 @@ import threading from pymodbus.client.sync import ModbusSerialClient, ModbusTcpClient, ModbusUdpClient from pymodbus.constants import Defaults from pymodbus.exceptions import ModbusException -from pymodbus.pdu import ExceptionResponse, IllegalFunctionRequest from pymodbus.transaction import ModbusRtuFramer from homeassistant.const import ( @@ -237,8 +236,8 @@ class ModbusHub: result = self._client.read_coils(address, count, **kwargs) except ModbusException as exception_error: self._log_error(exception_error) - return None - if isinstance(result, (ExceptionResponse, IllegalFunctionRequest)): + result = exception_error + if not hasattr(result, "bits"): self._log_error(result) return None self._in_error = False @@ -251,9 +250,8 @@ class ModbusHub: try: result = self._client.read_discrete_inputs(address, count, **kwargs) except ModbusException as exception_error: - self._log_error(exception_error) - return None - if isinstance(result, (ExceptionResponse, IllegalFunctionRequest)): + result = exception_error + if not hasattr(result, "bits"): self._log_error(result) return None self._in_error = False @@ -266,9 +264,8 @@ class ModbusHub: try: result = self._client.read_input_registers(address, count, **kwargs) except ModbusException as exception_error: - self._log_error(exception_error) - return None - if isinstance(result, (ExceptionResponse, IllegalFunctionRequest)): + result = exception_error + if not hasattr(result, "registers"): self._log_error(result) return None self._in_error = False @@ -281,9 +278,8 @@ class ModbusHub: try: result = self._client.read_holding_registers(address, count, **kwargs) except ModbusException as exception_error: - self._log_error(exception_error) - return None - if isinstance(result, (ExceptionResponse, IllegalFunctionRequest)): + result = exception_error + if not hasattr(result, "registers"): self._log_error(result) return None self._in_error = False @@ -296,9 +292,8 @@ class ModbusHub: try: result = self._client.write_coil(address, value, **kwargs) except ModbusException as exception_error: - self._log_error(exception_error) - return False - if isinstance(result, (ExceptionResponse, IllegalFunctionRequest)): + result = exception_error + if not hasattr(result, "value"): self._log_error(result) return False self._in_error = False @@ -311,9 +306,8 @@ class ModbusHub: try: result = self._client.write_coils(address, values, **kwargs) except ModbusException as exception_error: - self._log_error(exception_error) - return False - if isinstance(result, (ExceptionResponse, IllegalFunctionRequest)): + result = exception_error + if not hasattr(result, "count"): self._log_error(result) return False self._in_error = False @@ -326,9 +320,8 @@ class ModbusHub: try: result = self._client.write_register(address, value, **kwargs) except ModbusException as exception_error: - self._log_error(exception_error) - return False - if isinstance(result, (ExceptionResponse, IllegalFunctionRequest)): + result = exception_error + if not hasattr(result, "value"): self._log_error(result) return False self._in_error = False @@ -341,9 +334,8 @@ class ModbusHub: try: result = self._client.write_registers(address, values, **kwargs) except ModbusException as exception_error: - self._log_error(exception_error) - return False - if isinstance(result, (ExceptionResponse, IllegalFunctionRequest)): + result = exception_error + if not hasattr(result, "count"): self._log_error(result) return False self._in_error = False diff --git a/homeassistant/components/ovo_energy/manifest.json b/homeassistant/components/ovo_energy/manifest.json index 37950df84cc..ba559ffb41d 100644 --- a/homeassistant/components/ovo_energy/manifest.json +++ b/homeassistant/components/ovo_energy/manifest.json @@ -3,7 +3,7 @@ "name": "OVO Energy", "config_flow": true, "documentation": "https://www.home-assistant.io/integrations/ovo_energy", - "requirements": ["ovoenergy==1.1.11"], + "requirements": ["ovoenergy==1.1.12"], "codeowners": ["@timmo001"], "iot_class": "cloud_polling" } diff --git a/homeassistant/components/philips_js/manifest.json b/homeassistant/components/philips_js/manifest.json index d41ac0881ba..9d1c4dbd04d 100644 --- a/homeassistant/components/philips_js/manifest.json +++ b/homeassistant/components/philips_js/manifest.json @@ -2,7 +2,7 @@ "domain": "philips_js", "name": "Philips TV", "documentation": "https://www.home-assistant.io/integrations/philips_js", - "requirements": ["ha-philipsjs==2.7.0"], + "requirements": ["ha-philipsjs==2.7.3"], "codeowners": ["@elupus"], "config_flow": true, "iot_class": "local_polling" diff --git a/homeassistant/components/rachio/switch.py b/homeassistant/components/rachio/switch.py index 41b253d97ee..fd17699f592 100644 --- a/homeassistant/components/rachio/switch.py +++ b/homeassistant/components/rachio/switch.py @@ -418,9 +418,8 @@ class RachioZone(RachioSwitch): CONF_MANUAL_RUN_MINS, DEFAULT_MANUAL_RUN_MINS ) ) - self._controller.rachio.zone.start( - self.zone_id, manual_run_time.total_seconds() - ) + # The API limit is 3 hours, and requires an int be passed + self._controller.rachio.zone.start(self.zone_id, manual_run_time.seconds) _LOGGER.debug( "Watering %s on %s for %s", self.name, diff --git a/homeassistant/components/sonos/manifest.json b/homeassistant/components/sonos/manifest.json index d31881959b3..a87280aaabd 100644 --- a/homeassistant/components/sonos/manifest.json +++ b/homeassistant/components/sonos/manifest.json @@ -3,7 +3,7 @@ "name": "Sonos", "config_flow": true, "documentation": "https://www.home-assistant.io/integrations/sonos", - "requirements": ["pysonos==0.0.44"], + "requirements": ["pysonos==0.0.45"], "after_dependencies": ["plex"], "ssdp": [ { diff --git a/homeassistant/components/sonos/media_player.py b/homeassistant/components/sonos/media_player.py index b179f480724..11f59f2f432 100644 --- a/homeassistant/components/sonos/media_player.py +++ b/homeassistant/components/sonos/media_player.py @@ -498,7 +498,9 @@ class SonosMediaPlayerEntity(SonosEntity, MediaPlayerEntity): if new_status == "TRANSITIONING": return - self._play_mode = event.current_play_mode if event else self.soco.play_mode + self._play_mode = ( + variables["current_play_mode"] if variables else self.soco.play_mode + ) self._uri = None self._media_duration = None self._media_image_url = None diff --git a/homeassistant/components/sonos/speaker.py b/homeassistant/components/sonos/speaker.py index 73704c61364..03cce67e4d8 100644 --- a/homeassistant/components/sonos/speaker.py +++ b/homeassistant/components/sonos/speaker.py @@ -4,6 +4,7 @@ from __future__ import annotations from asyncio import gather import contextlib import datetime +from functools import partial import logging from typing import Any, Callable @@ -223,7 +224,11 @@ class SonosSpeaker: return self._poll_timer = self.hass.helpers.event.async_track_time_interval( - async_dispatcher_send(self.hass, f"{SONOS_ENTITY_UPDATE}-{self.soco.uid}"), + partial( + async_dispatcher_send, + self.hass, + f"{SONOS_ENTITY_UPDATE}-{self.soco.uid}", + ), SCAN_INTERVAL, ) diff --git a/homeassistant/components/systemmonitor/sensor.py b/homeassistant/components/systemmonitor/sensor.py index 70fd8275bd7..bfaf7231945 100644 --- a/homeassistant/components/systemmonitor/sensor.py +++ b/homeassistant/components/systemmonitor/sensor.py @@ -203,7 +203,7 @@ async def async_setup_platform( ) -> None: """Set up the system monitor sensors.""" entities = [] - sensor_registry: dict[str, SensorData] = {} + sensor_registry: dict[tuple[str, str], SensorData] = {} for resource in config[CONF_RESOURCES]: type_ = resource[CONF_TYPE] @@ -225,7 +225,9 @@ async def async_setup_platform( _LOGGER.warning("Cannot read CPU / processor temperature information") continue - sensor_registry[type_] = SensorData(argument, None, None, None, None) + sensor_registry[(type_, argument)] = SensorData( + argument, None, None, None, None + ) entities.append(SystemMonitorSensor(sensor_registry, type_, argument)) scan_interval = config.get(CONF_SCAN_INTERVAL, DEFAULT_SCAN_INTERVAL) @@ -236,7 +238,7 @@ async def async_setup_platform( async def async_setup_sensor_registry_updates( hass: HomeAssistant, - sensor_registry: dict[str, SensorData], + sensor_registry: dict[tuple[str, str], SensorData], scan_interval: datetime.timedelta, ) -> None: """Update the registry and create polling.""" @@ -245,11 +247,11 @@ async def async_setup_sensor_registry_updates( def _update_sensors() -> None: """Update sensors and store the result in the registry.""" - for type_, data in sensor_registry.items(): + for (type_, argument), data in sensor_registry.items(): try: state, value, update_time = _update(type_, data) except Exception as ex: # pylint: disable=broad-except - _LOGGER.exception("Error updating sensor: %s", type_) + _LOGGER.exception("Error updating sensor: %s (%s)", type_, argument) data.last_exception = ex else: data.state = state @@ -295,7 +297,7 @@ class SystemMonitorSensor(SensorEntity): def __init__( self, - sensor_registry: dict[str, SensorData], + sensor_registry: dict[tuple[str, str], SensorData], sensor_type: str, argument: str = "", ) -> None: @@ -304,6 +306,7 @@ class SystemMonitorSensor(SensorEntity): self._name: str = f"{self.sensor_type[SENSOR_TYPE_NAME]} {argument}".rstrip() self._unique_id: str = slugify(f"{sensor_type}_{argument}") self._sensor_registry = sensor_registry + self._argument: str = argument @property def name(self) -> str: @@ -353,7 +356,7 @@ class SystemMonitorSensor(SensorEntity): @property def data(self) -> SensorData: """Return registry entry for the data.""" - return self._sensor_registry[self._type] + return self._sensor_registry[(self._type, self._argument)] async def async_added_to_hass(self) -> None: """When entity is added to hass.""" diff --git a/homeassistant/components/tasmota/manifest.json b/homeassistant/components/tasmota/manifest.json index b3a4bb09fea..a6e7a1d45a8 100644 --- a/homeassistant/components/tasmota/manifest.json +++ b/homeassistant/components/tasmota/manifest.json @@ -3,7 +3,7 @@ "name": "Tasmota", "config_flow": true, "documentation": "https://www.home-assistant.io/integrations/tasmota", - "requirements": ["hatasmota==0.2.11"], + "requirements": ["hatasmota==0.2.12"], "dependencies": ["mqtt"], "mqtt": ["tasmota/discovery/#"], "codeowners": ["@emontnemery"], diff --git a/homeassistant/components/tesla/__init__.py b/homeassistant/components/tesla/__init__.py index 54e3bab3f44..d945d87243e 100644 --- a/homeassistant/components/tesla/__init__.py +++ b/homeassistant/components/tesla/__init__.py @@ -140,7 +140,7 @@ async def async_setup_entry(hass, config_entry): hass.data.setdefault(DOMAIN, {}) config = config_entry.data # Because users can have multiple accounts, we always create a new session so they have separate cookies - async_client = httpx.AsyncClient(headers={USER_AGENT: SERVER_SOFTWARE}) + async_client = httpx.AsyncClient(headers={USER_AGENT: SERVER_SOFTWARE}, timeout=60) email = config_entry.title if email in hass.data[DOMAIN] and CONF_SCAN_INTERVAL in hass.data[DOMAIN][email]: scan_interval = hass.data[DOMAIN][email][CONF_SCAN_INTERVAL] diff --git a/homeassistant/components/tesla/config_flow.py b/homeassistant/components/tesla/config_flow.py index bb0b434ae48..6ec696855cd 100644 --- a/homeassistant/components/tesla/config_flow.py +++ b/homeassistant/components/tesla/config_flow.py @@ -150,7 +150,7 @@ async def validate_input(hass: core.HomeAssistant, data): """ config = {} - async_client = httpx.AsyncClient(headers={USER_AGENT: SERVER_SOFTWARE}) + async_client = httpx.AsyncClient(headers={USER_AGENT: SERVER_SOFTWARE}, timeout=60) try: controller = TeslaAPI( diff --git a/homeassistant/components/tplink/__init__.py b/homeassistant/components/tplink/__init__.py index e68c30f48b5..f424f90d6d3 100644 --- a/homeassistant/components/tplink/__init__.py +++ b/homeassistant/components/tplink/__init__.py @@ -111,7 +111,7 @@ async def async_setup_entry(hass: HomeAssistant, config_entry: ConfigType): async def async_unload_entry(hass, entry): """Unload a config entry.""" - platforms = [platform for platform in PLATFORMS if platform in hass.data[DOMAIN]] + platforms = [platform for platform in PLATFORMS if hass.data[DOMAIN].get(platform)] unload_ok = await hass.config_entries.async_unload_platforms(entry, platforms) if unload_ok: hass.data[DOMAIN].clear() diff --git a/homeassistant/const.py b/homeassistant/const.py index 56fe2b36e57..51f762ad026 100644 --- a/homeassistant/const.py +++ b/homeassistant/const.py @@ -1,7 +1,7 @@ """Constants used by Home Assistant components.""" MAJOR_VERSION = 2021 MINOR_VERSION = 5 -PATCH_VERSION = "1" +PATCH_VERSION = "2" __short_version__ = f"{MAJOR_VERSION}.{MINOR_VERSION}" __version__ = f"{__short_version__}.{PATCH_VERSION}" REQUIRED_PYTHON_VER = (3, 8, 0) diff --git a/requirements_all.txt b/requirements_all.txt index 862119fd519..1e3328a6c3a 100644 --- a/requirements_all.txt +++ b/requirements_all.txt @@ -254,7 +254,7 @@ ambiclimate==0.2.1 amcrest==1.7.2 # homeassistant.components.androidtv -androidtv[async]==0.0.58 +androidtv[async]==0.0.59 # homeassistant.components.anel_pwrctrl anel_pwrctrl-homeassistant==0.0.1.dev2 @@ -479,7 +479,7 @@ defusedxml==0.6.0 deluge-client==1.7.1 # homeassistant.components.denonavr -denonavr==0.10.6 +denonavr==0.10.7 # homeassistant.components.devolo_home_control devolo-home-control-api==0.17.3 @@ -720,7 +720,7 @@ guppy3==3.1.0 ha-ffmpeg==3.0.2 # homeassistant.components.philips_js -ha-philipsjs==2.7.0 +ha-philipsjs==2.7.3 # homeassistant.components.habitica habitipy==0.2.0 @@ -735,7 +735,7 @@ hass-nabucasa==0.43.0 hass_splunk==0.1.1 # homeassistant.components.tasmota -hatasmota==0.2.11 +hatasmota==0.2.12 # homeassistant.components.jewish_calendar hdate==0.10.2 @@ -1088,7 +1088,7 @@ oru==0.1.11 orvibo==1.1.1 # homeassistant.components.ovo_energy -ovoenergy==1.1.11 +ovoenergy==1.1.12 # homeassistant.components.mqtt # homeassistant.components.shiftr @@ -1741,7 +1741,7 @@ pysnmp==4.4.12 pysoma==0.0.10 # homeassistant.components.sonos -pysonos==0.0.44 +pysonos==0.0.45 # homeassistant.components.spc pyspcwebgw==0.4.0 diff --git a/requirements_test_all.txt b/requirements_test_all.txt index 15f6ce2aa97..f199871ff9f 100644 --- a/requirements_test_all.txt +++ b/requirements_test_all.txt @@ -167,7 +167,7 @@ airly==1.1.0 ambiclimate==0.2.1 # homeassistant.components.androidtv -androidtv[async]==0.0.58 +androidtv[async]==0.0.59 # homeassistant.components.apns apns2==0.3.0 @@ -264,7 +264,7 @@ debugpy==1.2.1 defusedxml==0.6.0 # homeassistant.components.denonavr -denonavr==0.10.6 +denonavr==0.10.7 # homeassistant.components.devolo_home_control devolo-home-control-api==0.17.3 @@ -393,7 +393,7 @@ guppy3==3.1.0 ha-ffmpeg==3.0.2 # homeassistant.components.philips_js -ha-philipsjs==2.7.0 +ha-philipsjs==2.7.3 # homeassistant.components.habitica habitipy==0.2.0 @@ -405,7 +405,7 @@ hangups==0.4.11 hass-nabucasa==0.43.0 # homeassistant.components.tasmota -hatasmota==0.2.11 +hatasmota==0.2.12 # homeassistant.components.jewish_calendar hdate==0.10.2 @@ -576,7 +576,7 @@ onvif-zeep-async==1.0.0 openerz-api==0.1.0 # homeassistant.components.ovo_energy -ovoenergy==1.1.11 +ovoenergy==1.1.12 # homeassistant.components.mqtt # homeassistant.components.shiftr @@ -959,7 +959,7 @@ pysmartthings==0.7.6 pysoma==0.0.10 # homeassistant.components.sonos -pysonos==0.0.44 +pysonos==0.0.45 # homeassistant.components.spc pyspcwebgw==0.4.0 diff --git a/tests/components/tasmota/test_sensor.py b/tests/components/tasmota/test_sensor.py index 0d6820f2d34..425fa3f26f6 100644 --- a/tests/components/tasmota/test_sensor.py +++ b/tests/components/tasmota/test_sensor.py @@ -43,6 +43,15 @@ DEFAULT_SENSOR_CONFIG = { } } +BAD_INDEXED_SENSOR_CONFIG_3 = { + "sn": { + "Time": "2020-09-25T12:47:15", + "ENERGY": { + "ApparentPower": [7.84, 1.23, 2.34], + }, + } +} + INDEXED_SENSOR_CONFIG = { "sn": { "Time": "2020-09-25T12:47:15", @@ -224,6 +233,117 @@ async def test_indexed_sensor_state_via_mqtt(hass, mqtt_mock, setup_tasmota): assert state.state == "7.8" +async def test_bad_indexed_sensor_state_via_mqtt(hass, mqtt_mock, setup_tasmota): + """Test state update via MQTT where sensor is not matching configuration.""" + config = copy.deepcopy(DEFAULT_CONFIG) + sensor_config = copy.deepcopy(BAD_INDEXED_SENSOR_CONFIG_3) + mac = config["mac"] + + async_fire_mqtt_message( + hass, + f"{DEFAULT_PREFIX}/{mac}/config", + json.dumps(config), + ) + await hass.async_block_till_done() + async_fire_mqtt_message( + hass, + f"{DEFAULT_PREFIX}/{mac}/sensors", + json.dumps(sensor_config), + ) + await hass.async_block_till_done() + + state = hass.states.get("sensor.tasmota_energy_apparentpower_0") + assert state.state == "unavailable" + assert not state.attributes.get(ATTR_ASSUMED_STATE) + state = hass.states.get("sensor.tasmota_energy_apparentpower_1") + assert state.state == "unavailable" + assert not state.attributes.get(ATTR_ASSUMED_STATE) + state = hass.states.get("sensor.tasmota_energy_apparentpower_2") + 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_energy_apparentpower_0") + assert state.state == STATE_UNKNOWN + assert not state.attributes.get(ATTR_ASSUMED_STATE) + state = hass.states.get("sensor.tasmota_energy_apparentpower_1") + assert state.state == STATE_UNKNOWN + assert not state.attributes.get(ATTR_ASSUMED_STATE) + state = hass.states.get("sensor.tasmota_energy_apparentpower_2") + assert state.state == STATE_UNKNOWN + assert not state.attributes.get(ATTR_ASSUMED_STATE) + + # Test periodic state update + async_fire_mqtt_message( + hass, "tasmota_49A3BC/tele/SENSOR", '{"ENERGY":{"ApparentPower":[1.2,3.4,5.6]}}' + ) + state = hass.states.get("sensor.tasmota_energy_apparentpower_0") + assert state.state == "1.2" + state = hass.states.get("sensor.tasmota_energy_apparentpower_1") + assert state.state == "3.4" + state = hass.states.get("sensor.tasmota_energy_apparentpower_2") + assert state.state == "5.6" + + # Test periodic state update with too few values + async_fire_mqtt_message( + hass, "tasmota_49A3BC/tele/SENSOR", '{"ENERGY":{"ApparentPower":[7.8,9.0]}}' + ) + state = hass.states.get("sensor.tasmota_energy_apparentpower_0") + assert state.state == "7.8" + state = hass.states.get("sensor.tasmota_energy_apparentpower_1") + assert state.state == "9.0" + state = hass.states.get("sensor.tasmota_energy_apparentpower_2") + assert state.state == STATE_UNKNOWN + + async_fire_mqtt_message( + hass, "tasmota_49A3BC/tele/SENSOR", '{"ENERGY":{"ApparentPower":2.3}}' + ) + state = hass.states.get("sensor.tasmota_energy_apparentpower_0") + assert state.state == "2.3" + state = hass.states.get("sensor.tasmota_energy_apparentpower_1") + assert state.state == STATE_UNKNOWN + state = hass.states.get("sensor.tasmota_energy_apparentpower_2") + assert state.state == STATE_UNKNOWN + + # Test polled state update + async_fire_mqtt_message( + hass, + "tasmota_49A3BC/stat/STATUS10", + '{"StatusSNS":{"ENERGY":{"ApparentPower":[1.2,3.4,5.6]}}}', + ) + state = hass.states.get("sensor.tasmota_energy_apparentpower_0") + assert state.state == "1.2" + state = hass.states.get("sensor.tasmota_energy_apparentpower_1") + assert state.state == "3.4" + state = hass.states.get("sensor.tasmota_energy_apparentpower_2") + assert state.state == "5.6" + + # Test polled state update with too few values + async_fire_mqtt_message( + hass, + "tasmota_49A3BC/stat/STATUS10", + '{"StatusSNS":{"ENERGY":{"ApparentPower":[7.8,9.0]}}}', + ) + state = hass.states.get("sensor.tasmota_energy_apparentpower_0") + assert state.state == "7.8" + state = hass.states.get("sensor.tasmota_energy_apparentpower_1") + assert state.state == "9.0" + state = hass.states.get("sensor.tasmota_energy_apparentpower_2") + assert state.state == STATE_UNKNOWN + + async_fire_mqtt_message( + hass, + "tasmota_49A3BC/stat/STATUS10", + '{"StatusSNS":{"ENERGY":{"ApparentPower":2.3}}}', + ) + state = hass.states.get("sensor.tasmota_energy_apparentpower_0") + assert state.state == "2.3" + state = hass.states.get("sensor.tasmota_energy_apparentpower_1") + assert state.state == STATE_UNKNOWN + state = hass.states.get("sensor.tasmota_energy_apparentpower_2") + assert state.state == STATE_UNKNOWN + + @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."""