From 596e4883b1b2207c82b59dcdda0d9e651fb3e14f Mon Sep 17 00:00:00 2001 From: Nippey Date: Tue, 12 Aug 2025 11:33:51 +0200 Subject: [PATCH] Add more sensors to Tuya weather station (#150442) Co-authored-by: Franck Nijhof Co-authored-by: epenet <6771947+epenet@users.noreply.github.com> --- homeassistant/components/tuya/const.py | 10 + homeassistant/components/tuya/sensor.py | 50 ++++ homeassistant/components/tuya/strings.json | 12 + .../tuya/snapshots/test_sensor.ambr | 215 ++++++++++++++++++ 4 files changed, 287 insertions(+) diff --git a/homeassistant/components/tuya/const.py b/homeassistant/components/tuya/const.py index 6c4b1c02b22..b60b3bc518c 100644 --- a/homeassistant/components/tuya/const.py +++ b/homeassistant/components/tuya/const.py @@ -24,6 +24,7 @@ from homeassistant.const import ( UnitOfPressure, UnitOfTemperature, UnitOfVolume, + UnitOfVolumetricFlux, ) DOMAIN = "tuya" @@ -296,6 +297,8 @@ class DPCode(StrEnum): PUMP_RESET = "pump_reset" # Water pump reset PUMP_TIME = "pump_time" # Water pump duration OXYGEN = "oxygen" # Oxygen bar + RAIN_24H = "rain_24h" # Total daily rainfall in mm + RAIN_RATE = "rain_rate" # Rain intensity in mm/h RECORD_MODE = "record_mode" RECORD_SWITCH = "record_switch" # Recording switch RELAY_STATUS = "relay_status" @@ -415,6 +418,7 @@ class DPCode(StrEnum): UPPER_TEMP = "upper_temp" UPPER_TEMP_F = "upper_temp_f" UV = "uv" # UV sterilization + UV_INDEX = "uv_index" UV_RUNTIME = "uv_runtime" # UV runtime VA_BATTERY = "va_battery" VA_HUMIDITY = "va_humidity" @@ -439,6 +443,7 @@ class DPCode(StrEnum): WINDOW_STATE = "window_state" WINDSPEED = "windspeed" WINDSPEED_AVG = "windspeed_avg" + WIND_DIRECT = "wind_direct" WIRELESS_BATTERYLOCK = "wireless_batterylock" WIRELESS_ELECTRICITY = "wireless_electricity" WORK_MODE = "work_mode" # Working mode @@ -524,6 +529,11 @@ UNITS = ( aliases={"m3"}, device_classes={SensorDeviceClass.GAS}, ), + UnitOfMeasurement( + unit=UnitOfVolumetricFlux.MILLIMETERS_PER_HOUR, + aliases={"mm"}, + device_classes={SensorDeviceClass.PRECIPITATION_INTENSITY}, + ), UnitOfMeasurement( unit=LIGHT_LUX, aliases={"lux"}, diff --git a/homeassistant/components/tuya/sensor.py b/homeassistant/components/tuya/sensor.py index 82ddb1ee0ab..275b7c28e3a 100644 --- a/homeassistant/components/tuya/sensor.py +++ b/homeassistant/components/tuya/sensor.py @@ -2,7 +2,9 @@ from __future__ import annotations +from collections.abc import Callable from dataclasses import dataclass +from typing import Any from tuya_sharing import CustomerDevice, Manager from tuya_sharing.device import DeviceStatusRange @@ -42,6 +44,25 @@ from .const import ( from .entity import TuyaEntity from .models import ComplexTypeData, ElectricityTypeData, EnumTypeData, IntegerTypeData +_WIND_DIRECTIONS = { + "north": 0.0, + "north_north_east": 22.5, + "north_east": 45.0, + "east_north_east": 67.5, + "east": 90.0, + "east_south_east": 112.5, + "south_east": 135.0, + "south_south_east": 157.5, + "south": 180.0, + "south_south_west": 202.5, + "south_west": 225.0, + "west_south_west": 247.5, + "west": 270.0, + "west_north_west": 292.5, + "north_west": 315.0, + "north_north_west": 337.5, +} + @dataclass(frozen=True) class TuyaSensorEntityDescription(SensorEntityDescription): @@ -49,6 +70,7 @@ class TuyaSensorEntityDescription(SensorEntityDescription): complex_type: type[ComplexTypeData] | None = None subkey: str | None = None + state_conversion: Callable[[Any], StateType] | None = None # Commonly used battery sensors, that are reused in the sensors down below. @@ -931,6 +953,30 @@ SENSORS: dict[str, tuple[TuyaSensorEntityDescription, ...]] = { device_class=SensorDeviceClass.WIND_SPEED, state_class=SensorStateClass.MEASUREMENT, ), + TuyaSensorEntityDescription( + key=DPCode.RAIN_24H, + translation_key="precipitation_today", + device_class=SensorDeviceClass.PRECIPITATION, + state_class=SensorStateClass.TOTAL_INCREASING, + ), + TuyaSensorEntityDescription( + key=DPCode.RAIN_RATE, + translation_key="precipitation_intensity", + device_class=SensorDeviceClass.PRECIPITATION_INTENSITY, + state_class=SensorStateClass.MEASUREMENT, + ), + TuyaSensorEntityDescription( + key=DPCode.UV_INDEX, + translation_key="uv_index", + state_class=SensorStateClass.MEASUREMENT, + ), + TuyaSensorEntityDescription( + key=DPCode.WIND_DIRECT, + translation_key="wind_direction", + device_class=SensorDeviceClass.WIND_DIRECTION, + state_class=SensorStateClass.MEASUREMENT, + state_conversion=lambda state: _WIND_DIRECTIONS.get(str(state)), + ), *BATTERY_SENSORS, ), # Gas Detector @@ -1625,6 +1671,10 @@ class TuyaSensorEntity(TuyaEntity, SensorEntity): if value is None: return None + # Convert value, if required + if (convert := self.entity_description.state_conversion) is not None: + return convert(value) + # Scale integer/float value if isinstance(self._type_data, IntegerTypeData): return self._type_data.scale_value(value) diff --git a/homeassistant/components/tuya/strings.json b/homeassistant/components/tuya/strings.json index f185e8d5489..fa15e34694c 100644 --- a/homeassistant/components/tuya/strings.json +++ b/homeassistant/components/tuya/strings.json @@ -535,6 +535,18 @@ "air_pressure": { "name": "Air pressure" }, + "precipitation_today": { + "name": "Total precipitation today" + }, + "precipitation_intensity": { + "name": "[%key:component::sensor::entity_component::precipitation_intensity::name%]" + }, + "uv_index": { + "name": "UV index" + }, + "wind_direction": { + "name": "[%key:component::sensor::entity_component::wind_direction::name%]" + }, "pm25": { "name": "[%key:component::sensor::entity_component::pm25::name%]" }, diff --git a/tests/components/tuya/snapshots/test_sensor.ambr b/tests/components/tuya/snapshots/test_sensor.ambr index 53003ac095a..0d113454d4d 100644 --- a/tests/components/tuya/snapshots/test_sensor.ambr +++ b/tests/components/tuya/snapshots/test_sensor.ambr @@ -1867,6 +1867,62 @@ 'state': '0.0', }) # --- +# name: test_platform_setup_and_discovery[sensor.br_7_in_1_wlan_wetterstation_anthrazit_precipitation_intensity-entry] + EntityRegistryEntrySnapshot({ + 'aliases': set({ + }), + 'area_id': None, + 'capabilities': dict({ + 'state_class': , + }), + 'config_entry_id': , + 'config_subentry_id': , + 'device_class': None, + 'device_id': , + 'disabled_by': None, + 'domain': 'sensor', + 'entity_category': None, + 'entity_id': 'sensor.br_7_in_1_wlan_wetterstation_anthrazit_precipitation_intensity', + 'has_entity_name': True, + 'hidden_by': None, + 'icon': None, + 'id': , + 'labels': set({ + }), + 'name': None, + 'options': dict({ + 'sensor': dict({ + 'suggested_display_precision': 0, + }), + }), + 'original_device_class': , + 'original_icon': None, + 'original_name': 'Precipitation intensity', + 'platform': 'tuya', + 'previous_unique_id': None, + 'suggested_object_id': None, + 'supported_features': 0, + 'translation_key': 'precipitation_intensity', + 'unique_id': 'tuya.6tbtkuv3tal1aesfjxqrain_rate', + 'unit_of_measurement': , + }) +# --- +# name: test_platform_setup_and_discovery[sensor.br_7_in_1_wlan_wetterstation_anthrazit_precipitation_intensity-state] + StateSnapshot({ + 'attributes': ReadOnlyDict({ + 'device_class': 'precipitation_intensity', + 'friendly_name': 'BR 7-in-1 WLAN Wetterstation Anthrazit Precipitation intensity', + 'state_class': , + 'unit_of_measurement': , + }), + 'context': , + 'entity_id': 'sensor.br_7_in_1_wlan_wetterstation_anthrazit_precipitation_intensity', + 'last_changed': , + 'last_reported': , + 'last_updated': , + 'state': '0.0', + }) +# --- # name: test_platform_setup_and_discovery[sensor.br_7_in_1_wlan_wetterstation_anthrazit_probe_temperature-entry] EntityRegistryEntrySnapshot({ 'aliases': set({ @@ -2147,6 +2203,165 @@ 'state': '24.0', }) # --- +# name: test_platform_setup_and_discovery[sensor.br_7_in_1_wlan_wetterstation_anthrazit_total_precipitation_today-entry] + EntityRegistryEntrySnapshot({ + 'aliases': set({ + }), + 'area_id': None, + 'capabilities': dict({ + 'state_class': , + }), + 'config_entry_id': , + 'config_subentry_id': , + 'device_class': None, + 'device_id': , + 'disabled_by': None, + 'domain': 'sensor', + 'entity_category': None, + 'entity_id': 'sensor.br_7_in_1_wlan_wetterstation_anthrazit_total_precipitation_today', + 'has_entity_name': True, + 'hidden_by': None, + 'icon': None, + 'id': , + 'labels': set({ + }), + 'name': None, + 'options': dict({ + 'sensor': dict({ + 'suggested_display_precision': 0, + }), + }), + 'original_device_class': , + 'original_icon': None, + 'original_name': 'Total precipitation today', + 'platform': 'tuya', + 'previous_unique_id': None, + 'suggested_object_id': None, + 'supported_features': 0, + 'translation_key': 'precipitation_today', + 'unique_id': 'tuya.6tbtkuv3tal1aesfjxqrain_24h', + 'unit_of_measurement': 'mm', + }) +# --- +# name: test_platform_setup_and_discovery[sensor.br_7_in_1_wlan_wetterstation_anthrazit_total_precipitation_today-state] + StateSnapshot({ + 'attributes': ReadOnlyDict({ + 'device_class': 'precipitation', + 'friendly_name': 'BR 7-in-1 WLAN Wetterstation Anthrazit Total precipitation today', + 'state_class': , + 'unit_of_measurement': 'mm', + }), + 'context': , + 'entity_id': 'sensor.br_7_in_1_wlan_wetterstation_anthrazit_total_precipitation_today', + 'last_changed': , + 'last_reported': , + 'last_updated': , + 'state': '0.0', + }) +# --- +# name: test_platform_setup_and_discovery[sensor.br_7_in_1_wlan_wetterstation_anthrazit_uv_index-entry] + EntityRegistryEntrySnapshot({ + 'aliases': set({ + }), + 'area_id': None, + 'capabilities': dict({ + 'state_class': , + }), + 'config_entry_id': , + 'config_subentry_id': , + 'device_class': None, + 'device_id': , + 'disabled_by': None, + 'domain': 'sensor', + 'entity_category': None, + 'entity_id': 'sensor.br_7_in_1_wlan_wetterstation_anthrazit_uv_index', + 'has_entity_name': True, + 'hidden_by': None, + 'icon': None, + 'id': , + 'labels': set({ + }), + 'name': None, + 'options': dict({ + }), + 'original_device_class': None, + 'original_icon': None, + 'original_name': 'UV index', + 'platform': 'tuya', + 'previous_unique_id': None, + 'suggested_object_id': None, + 'supported_features': 0, + 'translation_key': 'uv_index', + 'unique_id': 'tuya.6tbtkuv3tal1aesfjxquv_index', + 'unit_of_measurement': '', + }) +# --- +# name: test_platform_setup_and_discovery[sensor.br_7_in_1_wlan_wetterstation_anthrazit_uv_index-state] + StateSnapshot({ + 'attributes': ReadOnlyDict({ + 'friendly_name': 'BR 7-in-1 WLAN Wetterstation Anthrazit UV index', + 'state_class': , + 'unit_of_measurement': '', + }), + 'context': , + 'entity_id': 'sensor.br_7_in_1_wlan_wetterstation_anthrazit_uv_index', + 'last_changed': , + 'last_reported': , + 'last_updated': , + 'state': '0.0', + }) +# --- +# name: test_platform_setup_and_discovery[sensor.br_7_in_1_wlan_wetterstation_anthrazit_wind_direction-entry] + EntityRegistryEntrySnapshot({ + 'aliases': set({ + }), + 'area_id': None, + 'capabilities': dict({ + 'state_class': , + }), + 'config_entry_id': , + 'config_subentry_id': , + 'device_class': None, + 'device_id': , + 'disabled_by': None, + 'domain': 'sensor', + 'entity_category': None, + 'entity_id': 'sensor.br_7_in_1_wlan_wetterstation_anthrazit_wind_direction', + 'has_entity_name': True, + 'hidden_by': None, + 'icon': None, + 'id': , + 'labels': set({ + }), + 'name': None, + 'options': dict({ + }), + 'original_device_class': None, + 'original_icon': None, + 'original_name': 'Wind direction', + 'platform': 'tuya', + 'previous_unique_id': None, + 'suggested_object_id': None, + 'supported_features': 0, + 'translation_key': 'wind_direction', + 'unique_id': 'tuya.6tbtkuv3tal1aesfjxqwind_direct', + 'unit_of_measurement': None, + }) +# --- +# name: test_platform_setup_and_discovery[sensor.br_7_in_1_wlan_wetterstation_anthrazit_wind_direction-state] + StateSnapshot({ + 'attributes': ReadOnlyDict({ + 'friendly_name': 'BR 7-in-1 WLAN Wetterstation Anthrazit Wind direction', + 'state_class': , + }), + 'context': , + 'entity_id': 'sensor.br_7_in_1_wlan_wetterstation_anthrazit_wind_direction', + 'last_changed': , + 'last_reported': , + 'last_updated': , + 'state': 'unknown', + }) +# --- # name: test_platform_setup_and_discovery[sensor.br_7_in_1_wlan_wetterstation_anthrazit_wind_speed-entry] EntityRegistryEntrySnapshot({ 'aliases': set({