diff --git a/homeassistant/components/accuweather/sensor.py b/homeassistant/components/accuweather/sensor.py index 09e9cda30ad..d6f9339409f 100644 --- a/homeassistant/components/accuweather/sensor.py +++ b/homeassistant/components/accuweather/sensor.py @@ -12,7 +12,7 @@ from homeassistant.const import ( CONF_NAME, DEVICE_CLASS_TEMPERATURE, ) -from homeassistant.core import HomeAssistant +from homeassistant.core import HomeAssistant, callback from homeassistant.helpers.entity import DeviceInfo from homeassistant.helpers.entity_platform import AddEntitiesCallback from homeassistant.helpers.typing import StateType @@ -81,16 +81,11 @@ class AccuWeatherSensor(CoordinatorEntity, SensorEntity): ) -> None: """Initialize.""" super().__init__(coordinator) + self._sensor_data = _get_sensor_data(coordinator.data, forecast_day, kind) if forecast_day is None: self._description = SENSOR_TYPES[kind] - self._sensor_data: dict[str, Any] - if kind == "Precipitation": - self._sensor_data = coordinator.data["PrecipitationSummary"][kind] - else: - self._sensor_data = coordinator.data[kind] else: self._description = FORECAST_SENSOR_TYPES[kind] - self._sensor_data = coordinator.data[ATTR_FORECAST][forecast_day][kind] self._unit_system = API_METRIC if coordinator.is_metric else API_IMPERIAL self._name = name self.kind = kind @@ -182,3 +177,24 @@ class AccuWeatherSensor(CoordinatorEntity, SensorEntity): def entity_registry_enabled_default(self) -> bool: """Return if the entity should be enabled when first added to the entity registry.""" return self._description[ATTR_ENABLED] + + @callback + def _handle_coordinator_update(self) -> None: + """Handle data update.""" + self._sensor_data = _get_sensor_data( + self.coordinator.data, self.forecast_day, self.kind + ) + self.async_write_ha_state() + + +def _get_sensor_data( + sensors: dict[str, Any], forecast_day: int | None, kind: str +) -> Any: + """Get sensor data.""" + if forecast_day is not None: + return sensors[ATTR_FORECAST][forecast_day][kind] + + if kind == "Precipitation": + return sensors["PrecipitationSummary"][kind] + + return sensors[kind] diff --git a/homeassistant/components/daikin/manifest.json b/homeassistant/components/daikin/manifest.json index ec0b2716053..704cfcf739c 100644 --- a/homeassistant/components/daikin/manifest.json +++ b/homeassistant/components/daikin/manifest.json @@ -3,7 +3,7 @@ "name": "Daikin AC", "config_flow": true, "documentation": "https://www.home-assistant.io/integrations/daikin", - "requirements": ["pydaikin==2.4.2"], + "requirements": ["pydaikin==2.4.3"], "codeowners": ["@fredrike"], "zeroconf": ["_dkapi._tcp.local."], "quality_scale": "platinum", diff --git a/homeassistant/components/mobile_app/__init__.py b/homeassistant/components/mobile_app/__init__.py index 951c6f3beaf..9633ec6556d 100644 --- a/homeassistant/components/mobile_app/__init__.py +++ b/homeassistant/components/mobile_app/__init__.py @@ -160,7 +160,7 @@ def handle_push_notification_channel(hass, connection, msg): registered_channels = hass.data[DOMAIN][DATA_PUSH_CHANNEL] if webhook_id in registered_channels: - registered_channels.pop(webhook_id)() + registered_channels.pop(webhook_id) @callback def forward_push_notification(data): diff --git a/homeassistant/components/omnilogic/switch.py b/homeassistant/components/omnilogic/switch.py index 771b02a24c1..8fffa384916 100644 --- a/homeassistant/components/omnilogic/switch.py +++ b/homeassistant/components/omnilogic/switch.py @@ -153,8 +153,8 @@ class OmniLogicPumpControl(OmniLogicSwitch): state_key=state_key, ) - self._max_speed = int(coordinator.data[item_id]["Max-Pump-Speed"]) - self._min_speed = int(coordinator.data[item_id]["Min-Pump-Speed"]) + self._max_speed = int(coordinator.data[item_id].get("Max-Pump-Speed", 100)) + self._min_speed = int(coordinator.data[item_id].get("Min-Pump-Speed", 0)) if "Filter-Type" in coordinator.data[item_id]: self._pump_type = PUMP_TYPES[coordinator.data[item_id]["Filter-Type"]] diff --git a/homeassistant/components/rfxtrx/manifest.json b/homeassistant/components/rfxtrx/manifest.json index 34c31c72a0d..0fc12e79d49 100644 --- a/homeassistant/components/rfxtrx/manifest.json +++ b/homeassistant/components/rfxtrx/manifest.json @@ -2,7 +2,7 @@ "domain": "rfxtrx", "name": "RFXCOM RFXtrx", "documentation": "https://www.home-assistant.io/integrations/rfxtrx", - "requirements": ["pyRFXtrx==0.26.1"], + "requirements": ["pyRFXtrx==0.27.0"], "codeowners": ["@danielhiversen", "@elupus", "@RobBie1221"], "config_flow": true, "iot_class": "local_push" diff --git a/homeassistant/components/sonos/speaker.py b/homeassistant/components/sonos/speaker.py index 055b4ce6845..dc610cee38a 100644 --- a/homeassistant/components/sonos/speaker.py +++ b/homeassistant/components/sonos/speaker.py @@ -382,6 +382,14 @@ class SonosSpeaker: """Update device properties from an event.""" if more_info := event.variables.get("more_info"): battery_dict = dict(x.split(":") for x in more_info.split(",")) + if "BattChg" not in battery_dict: + _LOGGER.debug( + "Unknown device properties update for %s (%s), please report an issue: '%s'", + self.zone_name, + self.model_name, + more_info, + ) + return await self.async_update_battery_info(battery_dict) self.async_write_entity_states() diff --git a/homeassistant/components/whois/sensor.py b/homeassistant/components/whois/sensor.py index 6d97037a4ee..4219c80193d 100644 --- a/homeassistant/components/whois/sensor.py +++ b/homeassistant/components/whois/sensor.py @@ -118,6 +118,7 @@ class WhoisSensor(SensorEntity): expiration_date = response["expiration_date"] if isinstance(expiration_date, list): attrs[ATTR_EXPIRES] = expiration_date[0].isoformat() + expiration_date = expiration_date[0] else: attrs[ATTR_EXPIRES] = expiration_date.isoformat() diff --git a/homeassistant/const.py b/homeassistant/const.py index 9c2c34c52f4..0bc06252960 100644 --- a/homeassistant/const.py +++ b/homeassistant/const.py @@ -5,7 +5,7 @@ from typing import Final MAJOR_VERSION: Final = 2021 MINOR_VERSION: Final = 6 -PATCH_VERSION: Final = "5" +PATCH_VERSION: Final = "6" __short_version__: Final = f"{MAJOR_VERSION}.{MINOR_VERSION}" __version__: Final = f"{__short_version__}.{PATCH_VERSION}" REQUIRED_PYTHON_VER: Final[tuple[int, int, int]] = (3, 8, 0) diff --git a/requirements_all.txt b/requirements_all.txt index 9b1cb90850d..65a981fa039 100644 --- a/requirements_all.txt +++ b/requirements_all.txt @@ -1253,7 +1253,7 @@ pyMetEireann==0.2 pyMetno==0.8.3 # homeassistant.components.rfxtrx -pyRFXtrx==0.26.1 +pyRFXtrx==0.27.0 # homeassistant.components.switchmate # pySwitchmate==0.4.6 @@ -1352,7 +1352,7 @@ pycsspeechtts==1.0.4 # pycups==1.9.73 # homeassistant.components.daikin -pydaikin==2.4.2 +pydaikin==2.4.3 # homeassistant.components.danfoss_air pydanfossair==0.1.0 diff --git a/requirements_test_all.txt b/requirements_test_all.txt index 6ba61f4642e..df1f7e6dcf2 100644 --- a/requirements_test_all.txt +++ b/requirements_test_all.txt @@ -693,7 +693,7 @@ pyMetEireann==0.2 pyMetno==0.8.3 # homeassistant.components.rfxtrx -pyRFXtrx==0.26.1 +pyRFXtrx==0.27.0 # homeassistant.components.tibber pyTibber==0.17.0 @@ -747,7 +747,7 @@ pycomfoconnect==0.4 pycoolmasternet-async==0.1.2 # homeassistant.components.daikin -pydaikin==2.4.2 +pydaikin==2.4.3 # homeassistant.components.deconz pydeconz==79 diff --git a/tests/components/accuweather/test_sensor.py b/tests/components/accuweather/test_sensor.py index 482fae696c0..64c49c61fe7 100644 --- a/tests/components/accuweather/test_sensor.py +++ b/tests/components/accuweather/test_sensor.py @@ -673,3 +673,36 @@ async def test_sensor_imperial_units(hass): assert state.attributes.get(ATTR_ATTRIBUTION) == ATTRIBUTION assert state.attributes.get(ATTR_ICON) == "mdi:weather-fog" assert state.attributes.get(ATTR_UNIT_OF_MEASUREMENT) == LENGTH_FEET + + +async def test_state_update(hass): + """Ensure the sensor state changes after updating the data.""" + await init_integration(hass) + + state = hass.states.get("sensor.home_cloud_ceiling") + assert state + assert state.state != STATE_UNAVAILABLE + assert state.state == "3200" + + future = utcnow() + timedelta(minutes=60) + + current_condition = json.loads( + load_fixture("accuweather/current_conditions_data.json") + ) + current_condition["Ceiling"]["Metric"]["Value"] = 3300 + + with patch( + "homeassistant.components.accuweather.AccuWeather.async_get_current_conditions", + return_value=current_condition, + ), patch( + "homeassistant.components.accuweather.AccuWeather.requests_remaining", + new_callable=PropertyMock, + return_value=10, + ): + async_fire_time_changed(hass, future) + await hass.async_block_till_done() + + state = hass.states.get("sensor.home_cloud_ceiling") + assert state + assert state.state != STATE_UNAVAILABLE + assert state.state == "3300" diff --git a/tests/components/mobile_app/test_notify.py b/tests/components/mobile_app/test_notify.py index 9c4ca146898..1e3b999d5f5 100644 --- a/tests/components/mobile_app/test_notify.py +++ b/tests/components/mobile_app/test_notify.py @@ -136,6 +136,18 @@ async def test_notify_ws_works( sub_result = await client.receive_json() assert sub_result["success"] + # Subscribe twice, it should forward all messages to 2nd subscription + await client.send_json( + { + "id": 6, + "type": "mobile_app/push_notification_channel", + "webhook_id": "mock-webhook_id", + } + ) + + sub_result = await client.receive_json() + assert sub_result["success"] + assert await hass.services.async_call( "notify", "mobile_app_test", {"message": "Hello world"}, blocking=True ) @@ -144,13 +156,14 @@ async def test_notify_ws_works( msg_result = await client.receive_json() assert msg_result["event"] == {"message": "Hello world"} + assert msg_result["id"] == 6 # This is the new subscription # Unsubscribe, now it should go over http await client.send_json( { - "id": 6, + "id": 7, "type": "unsubscribe_events", - "subscription": 5, + "subscription": 6, } ) sub_result = await client.receive_json() @@ -165,7 +178,7 @@ async def test_notify_ws_works( # Test non-existing webhook ID await client.send_json( { - "id": 7, + "id": 8, "type": "mobile_app/push_notification_channel", "webhook_id": "non-existing", } @@ -180,7 +193,7 @@ async def test_notify_ws_works( # Test webhook ID linked to other user await client.send_json( { - "id": 8, + "id": 9, "type": "mobile_app/push_notification_channel", "webhook_id": "webhook_id_2", } diff --git a/tests/components/rfxtrx/test_init.py b/tests/components/rfxtrx/test_init.py index b3829e2b5cc..3625c23ebb8 100644 --- a/tests/components/rfxtrx/test_init.py +++ b/tests/components/rfxtrx/test_init.py @@ -117,7 +117,7 @@ async def test_fire_event(hass, rfxtrx): "type_string": "Byron SX", "id_string": "00:90", "data": "0716000100900970", - "values": {"Command": "Chime", "Rssi numeric": 7, "Sound": 9}, + "values": {"Command": "Sound 9", "Rssi numeric": 7, "Sound": 9}, "device_id": device_id_2.id, }, ] diff --git a/tests/components/sonos/test_sensor.py b/tests/components/sonos/test_sensor.py index 12c12821a0d..8d402b589b0 100644 --- a/tests/components/sonos/test_sensor.py +++ b/tests/components/sonos/test_sensor.py @@ -83,3 +83,23 @@ async def test_battery_on_S1(hass, config_entry, config, soco, battery_event): power_state = hass.states.get(power.entity_id) assert power_state.state == STATE_OFF assert power_state.attributes.get(ATTR_BATTERY_POWER_SOURCE) == "BATTERY" + + +async def test_device_payload_without_battery( + hass, config_entry, config, soco, battery_event, caplog +): + """Test device properties event update without battery info.""" + soco.get_battery_info.return_value = None + + await setup_platform(hass, config_entry, config) + + subscription = soco.deviceProperties.subscribe.return_value + sub_callback = subscription.callback + + bad_payload = "BadKey:BadValue" + battery_event.variables["more_info"] = bad_payload + + sub_callback(battery_event) + await hass.async_block_till_done() + + assert bad_payload in caplog.text