Skip to content
Open
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
16 changes: 16 additions & 0 deletions tests/test_tuya.py
Original file line number Diff line number Diff line change
Expand Up @@ -2023,3 +2023,19 @@ async def test_ts601_door_sensor(
attrs = await cluster.read_attributes(attributes=[attribute])

assert attrs[0].get(attribute) == expected_value


async def test_tuya_quirk_builder_endpoint_id(zigpy_device_from_v2_quirk):
"""Test TuyaQuirkBuilder endpoint_id."""

(
zhaquirks.tuya.builder.TuyaQuirkBuilder("manufacturer", "model")
.adds_endpoint(2)
.tuya_humidity(dp_id=1, endpoint_id=2)
.add_to_registry()
)

device: Device = zigpy_device_from_v2_quirk("manufacturer", "model")

assert not hasattr(device.endpoints[1], "humidity")
assert hasattr(device.endpoints[2], "humidity")
74 changes: 55 additions & 19 deletions zhaquirks/tuya/builder/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -217,15 +217,17 @@ def _tuya_battery(
dp_id: int,
power_cfg: PowerConfiguration,
scale: float,
endpoint_id: int = 1,
) -> Self:
"""Add a Tuya Battery Power Configuration."""
self.tuya_dp(
dp_id,
power_cfg.ep_attribute,
PowerConfiguration.AttributeDefs.battery_percentage_remaining.name,
converter=lambda x: x * scale,
endpoint_id=endpoint_id,
)
self.adds(power_cfg)
self.adds(power_cfg, endpoint_id=endpoint_id)
return self

def tuya_battery(
Expand All @@ -236,11 +238,14 @@ def tuya_battery(
battery_qty: int | None = 2,
battery_voltage: int | None = None,
scale: float = 2,
endpoint_id: int = 1,
) -> Self:
"""Add a Tuya Battery Power Configuration."""

if power_cfg:
return self._tuya_battery(dp_id=dp_id, power_cfg=power_cfg, scale=scale)
return self._tuya_battery(
dp_id=dp_id, power_cfg=power_cfg, scale=scale, endpoint_id=endpoint_id
)

if not battery_voltage and (battery_type and battery_qty):
battery_voltage = BATTERY_VOLTAGES.get(battery_type)
Expand All @@ -255,7 +260,10 @@ class TuyaPowerConfigurationClusterBattery(TuyaPowerConfigurationCluster):
}

return self._tuya_battery(
dp_id=dp_id, power_cfg=TuyaPowerConfigurationClusterBattery, scale=scale
dp_id=dp_id,
power_cfg=TuyaPowerConfigurationClusterBattery,
scale=scale,
endpoint_id=endpoint_id,
)

def tuya_illuminance(
Expand All @@ -265,23 +273,26 @@ def tuya_illuminance(
converter: Callable[[Any], Any] | None = (
lambda x: 10000 * math.log10(x) + 1 if x != 0 else 0
),
endpoint_id: int = 1,
) -> Self:
"""Add a Tuya Illuminance Configuration."""
self.tuya_dp(
dp_id,
illuminance_cfg.ep_attribute,
IlluminanceMeasurement.AttributeDefs.measured_value.name,
converter=converter,
endpoint_id=endpoint_id,
)
self.adds(illuminance_cfg)
self.adds(illuminance_cfg, endpoint_id=endpoint_id)
return self

def tuya_contact(self, dp_id: int) -> Self:
def tuya_contact(self, dp_id: int, endpoint_id: int = 1) -> Self:
"""Add a Tuya IAS contact sensor."""
self.tuya_ias(
dp_id=dp_id,
ias_cfg=TuyaIasContact,
converter=lambda x: IasZone.ZoneStatus.Alarm_1 if x != 0 else 0,
endpoint_id=endpoint_id,
)
return self

Expand All @@ -290,31 +301,35 @@ def tuya_co2(
dp_id: int,
co2_cfg: TuyaLocalCluster = TuyaCO2Concentration,
scale: float = 1e-6,
endpoint_id: int = 1,
) -> Self:
"""Add a Tuya CO2 Configuration."""
self.tuya_dp(
dp_id,
co2_cfg.ep_attribute,
CarbonDioxideConcentration.AttributeDefs.measured_value.name,
converter=lambda x: x * scale,
endpoint_id=endpoint_id,
)
self.adds(co2_cfg)
self.adds(co2_cfg, endpoint_id=endpoint_id)
return self

def tuya_electrical_conductivity(
self,
dp_id: int,
ec_cfg: TuyaLocalCluster = TuyaElectricalConductivity,
scale: float = 1,
endpoint_id: int = 1,
) -> Self:
"""Add a Tuya Electrical Conductivity Configuration."""
self.tuya_dp(
dp_id,
ec_cfg.ep_attribute,
ElectricalConductivity.AttributeDefs.measured_value.name,
converter=lambda x: x * scale,
endpoint_id=endpoint_id,
)
self.adds(ec_cfg)
self.adds(ec_cfg, endpoint_id=endpoint_id)
return self

def tuya_formaldehyde(
Expand All @@ -326,48 +341,54 @@ def tuya_formaldehyde(
((MOL_VOL_AIR_NTP * x) / TuyaFormaldehydeConcentration.MOLECULAR_MASS), 2
)
* 1e-6,
endpoint_id: int = 1,
) -> Self:
"""Add a Tuya Formaldehyde Configuration."""
self.tuya_dp(
dp_id,
form_cfg.ep_attribute,
FormaldehydeConcentration.AttributeDefs.measured_value.name,
converter=converter,
endpoint_id=endpoint_id,
)
self.adds(form_cfg)
self.adds(form_cfg, endpoint_id=endpoint_id)
return self

def tuya_pm25(
self,
dp_id: int,
pm25_cfg: TuyaLocalCluster = TuyaPM25Concentration,
scale: float = 1,
endpoint_id: int = 1,
) -> Self:
"""Add a Tuya PM25 Configuration."""
self.tuya_dp(
dp_id,
pm25_cfg.ep_attribute,
PM25.AttributeDefs.measured_value.name,
converter=lambda x: x * scale,
endpoint_id=endpoint_id,
)
self.adds(pm25_cfg)
self.adds(pm25_cfg, endpoint_id=endpoint_id)
return self

def tuya_gas(self, dp_id: int) -> Self:
def tuya_gas(self, dp_id: int, endpoint_id: int = 1) -> Self:
"""Add a Tuya IAS gas sensor."""
self.tuya_ias(
dp_id=dp_id,
ias_cfg=TuyaIasGas,
converter=lambda x: IasZone.ZoneStatus.Alarm_1 if x == 0 else 0,
endpoint_id=endpoint_id,
)
return self

def tuya_smoke(self, dp_id: int) -> Self:
def tuya_smoke(self, dp_id: int, endpoint_id: int = 1) -> Self:
"""Add a Tuya IAS smoke/fire sensor."""
self.tuya_ias(
dp_id=dp_id,
ias_cfg=TuyaIasFire,
converter=lambda x: IasZone.ZoneStatus.Alarm_1 if x == 0 else 0,
endpoint_id=endpoint_id,
)
return self

Expand All @@ -376,101 +397,114 @@ def tuya_ias(
dp_id: int,
ias_cfg: TuyaLocalCluster,
converter: Callable[[Any], Any] | None = None,
endpoint_id: int = 1,
) -> Self:
"""Add a Tuya IAS Configuration."""
self.tuya_dp(
dp_id,
ias_cfg.ep_attribute,
IasZone.AttributeDefs.zone_status.name,
converter=converter,
endpoint_id=endpoint_id,
)
self.adds(ias_cfg)
self.adds(ias_cfg, endpoint_id=endpoint_id)
return self

def tuya_metering(
self,
dp_id: int,
metering_cfg: TuyaLocalCluster = TuyaValveWaterConsumedNoInstDemand,
scale: float = 1,
endpoint_id: int = 1,
) -> Self:
"""Add a Tuya Metering Configuration."""
self.tuya_dp(
dp_id,
metering_cfg.ep_attribute,
attribute_name="current_summ_delivered",
converter=lambda x: x * scale,
endpoint_id=endpoint_id,
)
self.adds(metering_cfg)
self.adds(metering_cfg, endpoint_id=endpoint_id)
return self

def tuya_onoff(
self,
dp_id: int,
onoff_cfg: TuyaLocalCluster = TuyaOnOffNM,
endpoint_id: int = 1,
) -> Self:
"""Add a Tuya OnOff Configuration."""
self.tuya_dp(
dp_id,
onoff_cfg.ep_attribute,
"on_off",
endpoint_id=endpoint_id,
)
self.adds(onoff_cfg)
self.adds(onoff_cfg, endpoint_id=endpoint_id)
return self

def tuya_humidity(
self,
dp_id: int,
rh_cfg: TuyaLocalCluster = TuyaRelativeHumidity,
scale: float = 100,
endpoint_id: int = 1,
) -> Self:
"""Add a Tuya Relative Humidity Configuration."""
self.tuya_dp(
dp_id,
rh_cfg.ep_attribute,
"measured_value",
converter=lambda x: x * scale,
endpoint_id=endpoint_id,
)
self.adds(rh_cfg)
self.adds(rh_cfg, endpoint_id=endpoint_id)
return self

def tuya_soil_moisture(
self,
dp_id: int,
soil_cfg: TuyaLocalCluster = TuyaSoilMoisture,
scale: float = 100,
endpoint_id: int = 1,
) -> Self:
"""Add a Tuya Soil Moisture Configuration."""
self.tuya_dp(
dp_id,
soil_cfg.ep_attribute,
"measured_value",
converter=lambda x: x * scale,
endpoint_id=endpoint_id,
)
self.adds(soil_cfg)
self.adds(soil_cfg, endpoint_id=endpoint_id)
return self

def tuya_temperature(
self,
dp_id: int,
temp_cfg: TuyaLocalCluster = TuyaTemperatureMeasurement,
scale: float = 100,
endpoint_id: int = 1,
) -> Self:
"""Add a Tuya Temperature Configuration."""
self.tuya_dp(
dp_id,
temp_cfg.ep_attribute,
"measured_value",
converter=lambda x: x * scale,
endpoint_id=endpoint_id,
)
self.adds(temp_cfg)
self.adds(temp_cfg, endpoint_id=endpoint_id)
return self

def tuya_vibration(self, dp_id: int) -> Self:
def tuya_vibration(self, dp_id: int, endpoint_id: int = 1) -> Self:
"""Add a Tuya IAS vibration sensor."""
self.tuya_ias(
dp_id=dp_id,
ias_cfg=TuyaIasVibration,
converter=lambda x: IasZone.ZoneStatus.Alarm_1 if x != 0 else 0,
endpoint_id=endpoint_id,
)
return self

Expand All @@ -479,15 +513,17 @@ def tuya_voc(
dp_id: int,
voc_cfg: TuyaLocalCluster = TuyaAirQualityVOC,
scale: float = 1e-6,
endpoint_id: int = 1,
) -> Self:
"""Add a Tuya VOC Configuration."""
self.tuya_dp(
dp_id,
voc_cfg.ep_attribute,
TuyaAirQualityVOC.AttributeDefs.measured_value.name,
converter=lambda x: x * scale,
endpoint_id=endpoint_id,
)
self.adds(voc_cfg)
self.adds(voc_cfg, endpoint_id=endpoint_id)
return self

def tuya_attribute(
Expand Down
Loading