Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions homeassistant/components/litterrobot/coordinator.py
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,9 @@ async def _async_update_data(self) -> None:
"""Update all device states from the Litter-Robot API."""
await self.account.refresh_robots()
await self.account.load_pets()
for pet in self.account.pets:
# Need to fetch weight history for `get_visits_since`
await pet.fetch_weight_history()

async def _async_setup(self) -> None:
"""Set up the coordinator."""
Expand Down
3 changes: 3 additions & 0 deletions homeassistant/components/litterrobot/icons.json
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,9 @@
},
"total_cycles": {
"default": "mdi:counter"
},
"visits_today": {
"default": "mdi:counter"
}
},
"switch": {
Expand Down
16 changes: 15 additions & 1 deletion homeassistant/components/litterrobot/sensor.py
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@
from homeassistant.const import PERCENTAGE, EntityCategory, UnitOfMass
from homeassistant.core import HomeAssistant
from homeassistant.helpers.entity_platform import AddConfigEntryEntitiesCallback
from homeassistant.util import dt as dt_util

from .coordinator import LitterRobotConfigEntry
from .entity import LitterRobotEntity, _WhiskerEntityT
Expand All @@ -39,6 +40,7 @@ class RobotSensorEntityDescription(SensorEntityDescription, Generic[_WhiskerEnti
"""A class that describes robot sensor entities."""

icon_fn: Callable[[Any], str | None] = lambda _: None
last_reset_fn: Callable[[], datetime | None] = lambda: None
value_fn: Callable[[_WhiskerEntityT], float | datetime | str | None]


Expand Down Expand Up @@ -179,7 +181,14 @@ class RobotSensorEntityDescription(SensorEntityDescription, Generic[_WhiskerEnti
native_unit_of_measurement=UnitOfMass.POUNDS,
state_class=SensorStateClass.MEASUREMENT,
value_fn=lambda pet: pet.weight,
)
),
RobotSensorEntityDescription[Pet](
key="visits_today",
translation_key="visits_today",
state_class=SensorStateClass.TOTAL,
last_reset_fn=dt_util.start_of_local_day,
value_fn=lambda pet: pet.get_visits_since(dt_util.start_of_local_day()),
),
]


Expand Down Expand Up @@ -225,3 +234,8 @@ def icon(self) -> str | None:
if (icon := self.entity_description.icon_fn(self.state)) is not None:
return icon
return super().icon

@property
def last_reset(self) -> datetime | None:
"""Return the time when the sensor was last reset, if any."""
return self.entity_description.last_reset_fn() or super().last_reset
4 changes: 4 additions & 0 deletions homeassistant/components/litterrobot/strings.json
Original file line number Diff line number Diff line change
Expand Up @@ -122,6 +122,10 @@
"name": "Total cycles",
"unit_of_measurement": "cycles"
},
"visits_today": {
"name": "Visits today",
"unit_of_measurement": "visits"
},
"waste_drawer": {
"name": "Waste drawer"
}
Expand Down
8 changes: 0 additions & 8 deletions homeassistant/components/miele/config_flow.py
Original file line number Diff line number Diff line change
Expand Up @@ -26,14 +26,6 @@ def logger(self) -> logging.Logger:
"""Return logger."""
return logging.getLogger(__name__)

@property
def extra_authorize_data(self) -> dict:
"""Extra data that needs to be appended to the authorize url."""
# "vg" is mandatory but the value doesn't seem to matter
return {
"vg": "sv-SE",
}

async def async_step_reauth(
self, entry_data: Mapping[str, Any]
) -> ConfigFlowResult:
Expand Down
20 changes: 10 additions & 10 deletions homeassistant/components/rainmachine/strings.json
Original file line number Diff line number Diff line change
Expand Up @@ -196,24 +196,24 @@
"description": "UNIX timestamp for the weather data. If omitted, the RainMachine device's local time at the time of the call is used."
},
"mintemp": {
"name": "Min temp",
"description": "Minimum temperature (°C)."
"name": "Min temperature",
"description": "Minimum temperature in current period (°C)."
},
"maxtemp": {
"name": "Max temp",
"description": "Maximum temperature (°C)."
"name": "Max temperature",
"description": "Maximum temperature in current period (°C)."
},
"temperature": {
"name": "Temperature",
"description": "Current temperature (°C)."
},
"wind": {
"name": "Wind speed",
"description": "Wind speed (m/s)."
"description": "Current wind speed (m/s)."
},
"solarrad": {
"name": "Solar radiation",
"description": "Solar radiation (MJ/m²/h)."
"description": "Current solar radiation (MJ/m²/h)."
},
"et": {
"name": "Evapotranspiration",
Expand All @@ -229,23 +229,23 @@
},
"minrh": {
"name": "Min relative humidity",
"description": "Min relative humidity (%RH)."
"description": "Minimum relative humidity in current period (%RH)."
},
"maxrh": {
"name": "Max relative humidity",
"description": "Max relative humidity (%RH)."
"description": "Maximum relative humidity in current period (%RH)."
},
"condition": {
"name": "Weather condition code",
"description": "Current weather condition code (WNUM)."
},
"pressure": {
"name": "Barametric pressure",
"description": "Barametric pressure (kPa)."
"description": "Current barametric pressure (kPa)."
},
"dewpoint": {
"name": "Dew point",
"description": "Dew point (°C)."
"description": "Current dew point (°C)."
}
}
},
Expand Down
3 changes: 2 additions & 1 deletion homeassistant/components/tuya/alarm_control_panel.py
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,8 @@

from . import TuyaConfigEntry
from .const import TUYA_DISCOVERY_NEW, DPCode, DPType
from .entity import EnumTypeData, TuyaEntity
from .entity import TuyaEntity
from .models import EnumTypeData


@dataclass(frozen=True)
Expand Down
75 changes: 70 additions & 5 deletions homeassistant/components/tuya/binary_sensor.py
Original file line number Diff line number Diff line change
Expand Up @@ -15,9 +15,10 @@
from homeassistant.core import HomeAssistant, callback
from homeassistant.helpers.dispatcher import async_dispatcher_connect
from homeassistant.helpers.entity_platform import AddConfigEntryEntitiesCallback
from homeassistant.util.json import json_loads

from . import TuyaConfigEntry
from .const import TUYA_DISCOVERY_NEW, DPCode
from .const import TUYA_DISCOVERY_NEW, DPCode, DPType
from .entity import TuyaEntity


Expand All @@ -31,6 +32,9 @@ class TuyaBinarySensorEntityDescription(BinarySensorEntityDescription):
# Value or values to consider binary sensor to be "on"
on_value: bool | float | int | str | set[bool | float | int | str] = True

# For DPType.BITMAP, the bitmap_key is used to extract the bit mask
bitmap_key: str | None = None


# Commonly used sensors
TAMPER_BINARY_SENSOR = TuyaBinarySensorEntityDescription(
Expand Down Expand Up @@ -71,6 +75,34 @@ class TuyaBinarySensorEntityDescription(BinarySensorEntityDescription):
),
TAMPER_BINARY_SENSOR,
),
# Dehumidifier
# https://developer.tuya.com/en/docs/iot/categorycs?id=Kaiuz1vcz4dha
"cs": (
TuyaBinarySensorEntityDescription(
key="tankfull",
dpcode=DPCode.FAULT,
device_class=BinarySensorDeviceClass.PROBLEM,
entity_category=EntityCategory.DIAGNOSTIC,
bitmap_key="tankfull",
translation_key="tankfull",
),
TuyaBinarySensorEntityDescription(
key="defrost",
dpcode=DPCode.FAULT,
device_class=BinarySensorDeviceClass.PROBLEM,
entity_category=EntityCategory.DIAGNOSTIC,
bitmap_key="defrost",
translation_key="defrost",
),
TuyaBinarySensorEntityDescription(
key="wet",
dpcode=DPCode.FAULT,
device_class=BinarySensorDeviceClass.PROBLEM,
entity_category=EntityCategory.DIAGNOSTIC,
bitmap_key="wet",
translation_key="wet",
),
),
# Smart Pet Feeder
# https://developer.tuya.com/en/docs/iot/categorycwwsq?id=Kaiuz2b6vydld
"cwwsq": (
Expand Down Expand Up @@ -343,6 +375,22 @@ class TuyaBinarySensorEntityDescription(BinarySensorEntityDescription):
}


def _get_bitmap_bit_mask(
device: CustomerDevice, dpcode: str, bitmap_key: str | None
) -> int | None:
"""Get the bit mask for a given bitmap description."""
if (
bitmap_key is None
or (status_range := device.status_range.get(dpcode)) is None
or status_range.type != DPType.BITMAP
or not isinstance(bitmap_values := json_loads(status_range.values), dict)
or not isinstance(bitmap_labels := bitmap_values.get("label"), list)
or bitmap_key not in bitmap_labels
):
return None
return bitmap_labels.index(bitmap_key)


async def async_setup_entry(
hass: HomeAssistant,
entry: TuyaConfigEntry,
Expand All @@ -361,12 +409,23 @@ def async_discover_device(device_ids: list[str]) -> None:
for description in descriptions:
dpcode = description.dpcode or description.key
if dpcode in device.status:
entities.append(
TuyaBinarySensorEntity(
device, hass_data.manager, description
)
mask = _get_bitmap_bit_mask(
device, dpcode, description.bitmap_key
)

if (
description.bitmap_key is None # Regular binary sensor
or mask is not None # Bitmap sensor with valid mask
):
entities.append(
TuyaBinarySensorEntity(
device,
hass_data.manager,
description,
mask,
)
)

async_add_entities(entities)

async_discover_device([*hass_data.manager.device_map])
Expand All @@ -386,11 +445,13 @@ def __init__(
device: CustomerDevice,
device_manager: Manager,
description: TuyaBinarySensorEntityDescription,
bit_mask: int | None = None,
) -> None:
"""Init Tuya binary sensor."""
super().__init__(device, device_manager)
self.entity_description = description
self._attr_unique_id = f"{super().unique_id}{description.key}"
self._bit_mask = bit_mask

@property
def is_on(self) -> bool:
Expand All @@ -399,6 +460,10 @@ def is_on(self) -> bool:
if dpcode not in self.device.status:
return False

if self._bit_mask is not None:
# For bitmap sensors, check the specific bit mask
return (self.device.status[dpcode] & (1 << self._bit_mask)) != 0

if isinstance(self.entity_description.on_value, set):
return self.device.status[dpcode] in self.entity_description.on_value

Expand Down
3 changes: 2 additions & 1 deletion homeassistant/components/tuya/climate.py
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,8 @@

from . import TuyaConfigEntry
from .const import TUYA_DISCOVERY_NEW, DPCode, DPType
from .entity import IntegerTypeData, TuyaEntity
from .entity import TuyaEntity
from .models import IntegerTypeData

TUYA_HVAC_TO_HA = {
"auto": HVACMode.HEAT_COOL,
Expand Down
1 change: 1 addition & 0 deletions homeassistant/components/tuya/const.py
Original file line number Diff line number Diff line change
Expand Up @@ -82,6 +82,7 @@ class WorkMode(StrEnum):
class DPType(StrEnum):
"""Data point types."""

BITMAP = "Bitmap"
BOOLEAN = "Boolean"
ENUM = "Enum"
INTEGER = "Integer"
Expand Down
3 changes: 2 additions & 1 deletion homeassistant/components/tuya/cover.py
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,8 @@

from . import TuyaConfigEntry
from .const import TUYA_DISCOVERY_NEW, DPCode, DPType
from .entity import IntegerTypeData, TuyaEntity
from .entity import TuyaEntity
from .models import IntegerTypeData


@dataclass(frozen=True)
Expand Down
Loading
Loading