mirror of
https://github.com/home-assistant/core.git
synced 2025-07-28 07:37:34 +00:00
Add support for ELV-SH-WSM to homematicip (#149098)
This commit is contained in:
parent
c075134845
commit
3f67ba4c02
@ -19,6 +19,7 @@ PLATFORMS = [
|
||||
Platform.LOCK,
|
||||
Platform.SENSOR,
|
||||
Platform.SWITCH,
|
||||
Platform.VALVE,
|
||||
Platform.WEATHER,
|
||||
]
|
||||
|
||||
|
@ -33,6 +33,7 @@ from homematicip.device import (
|
||||
TemperatureHumiditySensorOutdoor,
|
||||
TemperatureHumiditySensorWithoutDisplay,
|
||||
TiltVibrationSensor,
|
||||
WateringActuator,
|
||||
WeatherSensor,
|
||||
WeatherSensorPlus,
|
||||
WeatherSensorPro,
|
||||
@ -167,6 +168,29 @@ def get_device_handlers(hap: HomematicipHAP) -> dict[type, Callable]:
|
||||
HomematicipTiltStateSensor(hap, device),
|
||||
HomematicipTiltAngleSensor(hap, device),
|
||||
],
|
||||
WateringActuator: lambda device: [
|
||||
entity
|
||||
for ch in device.functionalChannels
|
||||
if ch.functionalChannelType
|
||||
== FunctionalChannelType.WATERING_ACTUATOR_CHANNEL
|
||||
for entity in (
|
||||
HomematicipWaterFlowSensor(
|
||||
hap, device, channel=ch.index, post="currentWaterFlow"
|
||||
),
|
||||
HomematicipWaterVolumeSensor(
|
||||
hap,
|
||||
device,
|
||||
channel=ch.index,
|
||||
post="waterVolume",
|
||||
attribute="waterVolume",
|
||||
),
|
||||
HomematicipWaterVolumeSinceOpenSensor(
|
||||
hap,
|
||||
device,
|
||||
channel=ch.index,
|
||||
),
|
||||
)
|
||||
],
|
||||
WeatherSensor: lambda device: [
|
||||
HomematicipTemperatureSensor(hap, device),
|
||||
HomematicipHumiditySensor(hap, device),
|
||||
@ -267,6 +291,65 @@ async def async_setup_entry(
|
||||
async_add_entities(entities)
|
||||
|
||||
|
||||
class HomematicipWaterFlowSensor(HomematicipGenericEntity, SensorEntity):
|
||||
"""Representation of the HomematicIP watering flow sensor."""
|
||||
|
||||
_attr_native_unit_of_measurement = UnitOfVolumeFlowRate.LITERS_PER_MINUTE
|
||||
_attr_state_class = SensorStateClass.MEASUREMENT
|
||||
|
||||
def __init__(
|
||||
self, hap: HomematicipHAP, device: Device, channel: int, post: str
|
||||
) -> None:
|
||||
"""Initialize the watering flow sensor device."""
|
||||
super().__init__(hap, device, post=post, channel=channel, is_multi_channel=True)
|
||||
|
||||
@property
|
||||
def native_value(self) -> float | None:
|
||||
"""Return the state."""
|
||||
return self.functional_channel.waterFlow
|
||||
|
||||
|
||||
class HomematicipWaterVolumeSensor(HomematicipGenericEntity, SensorEntity):
|
||||
"""Representation of the HomematicIP watering volume sensor."""
|
||||
|
||||
_attr_native_unit_of_measurement = UnitOfVolume.LITERS
|
||||
_attr_state_class = SensorStateClass.TOTAL_INCREASING
|
||||
|
||||
def __init__(
|
||||
self,
|
||||
hap: HomematicipHAP,
|
||||
device: Device,
|
||||
channel: int,
|
||||
post: str,
|
||||
attribute: str,
|
||||
) -> None:
|
||||
"""Initialize the watering volume sensor device."""
|
||||
super().__init__(hap, device, post=post, channel=channel, is_multi_channel=True)
|
||||
self._attribute_name = attribute
|
||||
|
||||
@property
|
||||
def native_value(self) -> float | None:
|
||||
"""Return the state."""
|
||||
return getattr(self.functional_channel, self._attribute_name, None)
|
||||
|
||||
|
||||
class HomematicipWaterVolumeSinceOpenSensor(HomematicipWaterVolumeSensor):
|
||||
"""Representation of the HomematicIP watering volume since open sensor."""
|
||||
|
||||
_attr_native_unit_of_measurement = UnitOfVolume.LITERS
|
||||
_attr_state_class = SensorStateClass.TOTAL_INCREASING
|
||||
|
||||
def __init__(self, hap: HomematicipHAP, device: Device, channel: int) -> None:
|
||||
"""Initialize the watering flow volume since open device."""
|
||||
super().__init__(
|
||||
hap,
|
||||
device,
|
||||
channel=channel,
|
||||
post="waterVolumeSinceOpen",
|
||||
attribute="waterVolumeSinceOpen",
|
||||
)
|
||||
|
||||
|
||||
class HomematicipTiltAngleSensor(HomematicipGenericEntity, SensorEntity):
|
||||
"""Representation of the HomematicIP tilt angle sensor."""
|
||||
|
||||
|
59
homeassistant/components/homematicip_cloud/valve.py
Normal file
59
homeassistant/components/homematicip_cloud/valve.py
Normal file
@ -0,0 +1,59 @@
|
||||
"""Support for HomematicIP Cloud valve devices."""
|
||||
|
||||
from homematicip.base.functionalChannels import FunctionalChannelType
|
||||
from homematicip.device import Device
|
||||
|
||||
from homeassistant.components.valve import (
|
||||
ValveDeviceClass,
|
||||
ValveEntity,
|
||||
ValveEntityFeature,
|
||||
)
|
||||
from homeassistant.core import HomeAssistant
|
||||
from homeassistant.helpers.entity_platform import AddConfigEntryEntitiesCallback
|
||||
|
||||
from .entity import HomematicipGenericEntity
|
||||
from .hap import HomematicIPConfigEntry, HomematicipHAP
|
||||
|
||||
|
||||
async def async_setup_entry(
|
||||
hass: HomeAssistant,
|
||||
config_entry: HomematicIPConfigEntry,
|
||||
async_add_entities: AddConfigEntryEntitiesCallback,
|
||||
) -> None:
|
||||
"""Set up the HomematicIP valves from a config entry."""
|
||||
hap = config_entry.runtime_data
|
||||
entities = [
|
||||
HomematicipWateringValve(hap, device, ch.index)
|
||||
for device in hap.home.devices
|
||||
for ch in device.functionalChannels
|
||||
if ch.functionalChannelType == FunctionalChannelType.WATERING_ACTUATOR_CHANNEL
|
||||
]
|
||||
|
||||
async_add_entities(entities)
|
||||
|
||||
|
||||
class HomematicipWateringValve(HomematicipGenericEntity, ValveEntity):
|
||||
"""Representation of a HomematicIP valve."""
|
||||
|
||||
_attr_reports_position = False
|
||||
_attr_supported_features = ValveEntityFeature.OPEN | ValveEntityFeature.CLOSE
|
||||
_attr_device_class = ValveDeviceClass.WATER
|
||||
|
||||
def __init__(self, hap: HomematicipHAP, device: Device, channel: int) -> None:
|
||||
"""Initialize the valve."""
|
||||
super().__init__(
|
||||
hap, device=device, channel=channel, post="watering", is_multi_channel=True
|
||||
)
|
||||
|
||||
async def async_open_valve(self) -> None:
|
||||
"""Open the valve."""
|
||||
await self.functional_channel.set_watering_switch_state_async(True)
|
||||
|
||||
async def async_close_valve(self) -> None:
|
||||
"""Close valve."""
|
||||
await self.functional_channel.set_watering_switch_state_async(False)
|
||||
|
||||
@property
|
||||
def is_closed(self) -> bool:
|
||||
"""Return if the valve is closed."""
|
||||
return self.functional_channel.wateringActive is False
|
@ -8936,6 +8936,161 @@
|
||||
"serializedGlobalTradeItemNumber": "3014F71100000000000RGBW2",
|
||||
"type": "RGBW_DIMMER",
|
||||
"updateState": "UP_TO_DATE"
|
||||
},
|
||||
"3014F71100000000000SHWSM": {
|
||||
"availableFirmwareVersion": "0.0.0",
|
||||
"connectionType": "HMIP_RF",
|
||||
"deviceArchetype": "HMIP",
|
||||
"firmwareVersion": "1.0.10",
|
||||
"firmwareVersionInteger": 65546,
|
||||
"functionalChannels": {
|
||||
"0": {
|
||||
"altitude": null,
|
||||
"busConfigMismatch": null,
|
||||
"coProFaulty": false,
|
||||
"coProRestartNeeded": false,
|
||||
"coProUpdateFailure": false,
|
||||
"configPending": false,
|
||||
"controlsMountingOrientation": null,
|
||||
"daliBusState": null,
|
||||
"dataDecodingFailedError": null,
|
||||
"defaultLinkedGroup": [],
|
||||
"deviceAliveSignalEnabled": null,
|
||||
"deviceCommunicationError": null,
|
||||
"deviceDriveError": null,
|
||||
"deviceDriveModeError": null,
|
||||
"deviceId": "3014F71100000000000SHWSM",
|
||||
"deviceOperationMode": null,
|
||||
"deviceOverheated": false,
|
||||
"deviceOverloaded": false,
|
||||
"devicePowerFailureDetected": false,
|
||||
"deviceUndervoltage": false,
|
||||
"displayContrast": null,
|
||||
"displayMode": null,
|
||||
"displayMountingOrientation": null,
|
||||
"dutyCycle": false,
|
||||
"frostProtectionError": false,
|
||||
"frostProtectionErrorAcknowledged": null,
|
||||
"functionalChannelType": "DEVICE_BASE",
|
||||
"groupIndex": 0,
|
||||
"groups": ["00000000-0000-0000-0000-000000000022"],
|
||||
"index": 0,
|
||||
"inputLayoutMode": null,
|
||||
"invertedDisplayColors": null,
|
||||
"label": "",
|
||||
"lockJammed": null,
|
||||
"lowBat": false,
|
||||
"mountingModuleError": null,
|
||||
"mountingOrientation": null,
|
||||
"multicastRoutingEnabled": false,
|
||||
"noDataFromLinkyError": null,
|
||||
"operationDays": null,
|
||||
"particulateMatterSensorCommunicationError": null,
|
||||
"particulateMatterSensorError": null,
|
||||
"powerShortCircuit": null,
|
||||
"profilePeriodLimitReached": null,
|
||||
"routerModuleEnabled": false,
|
||||
"routerModuleSupported": false,
|
||||
"rssiDeviceValue": -46,
|
||||
"rssiPeerValue": -43,
|
||||
"sensorCommunicationError": null,
|
||||
"sensorError": null,
|
||||
"shortCircuitDataLine": null,
|
||||
"supportedOptionalFeatures": {
|
||||
"IFeatureBusConfigMismatch": false,
|
||||
"IFeatureDataDecodingFailedError": false,
|
||||
"IFeatureDeviceCoProError": false,
|
||||
"IFeatureDeviceCoProRestart": false,
|
||||
"IFeatureDeviceCoProUpdate": false,
|
||||
"IFeatureDeviceCommunicationError": false,
|
||||
"IFeatureDeviceDaliBusError": false,
|
||||
"IFeatureDeviceDriveError": false,
|
||||
"IFeatureDeviceDriveModeError": false,
|
||||
"IFeatureDeviceIdentify": false,
|
||||
"IFeatureDeviceMountingModuleError": false,
|
||||
"IFeatureDeviceOverheated": true,
|
||||
"IFeatureDeviceOverloaded": false,
|
||||
"IFeatureDeviceParticulateMatterSensorCommunicationError": false,
|
||||
"IFeatureDeviceParticulateMatterSensorError": false,
|
||||
"IFeatureDevicePowerFailure": false,
|
||||
"IFeatureDeviceSensorCommunicationError": false,
|
||||
"IFeatureDeviceSensorError": false,
|
||||
"IFeatureDeviceTempSensorError": false,
|
||||
"IFeatureDeviceTemperatureHumiditySensorCommunicationError": false,
|
||||
"IFeatureDeviceTemperatureHumiditySensorError": false,
|
||||
"IFeatureDeviceTemperatureOutOfRange": false,
|
||||
"IFeatureDeviceUndervoltage": true,
|
||||
"IFeatureMulticastRouter": false,
|
||||
"IFeatureNoDataFromLinkyError": false,
|
||||
"IFeaturePowerShortCircuit": false,
|
||||
"IFeatureProfilePeriodLimit": false,
|
||||
"IFeatureRssiValue": true,
|
||||
"IFeatureShortCircuitDataLine": false,
|
||||
"IFeatureTicVersionError": false,
|
||||
"IOptionalFeatureAltitude": false,
|
||||
"IOptionalFeatureDefaultLinkedGroup": false,
|
||||
"IOptionalFeatureDeviceAliveSignalEnabled": false,
|
||||
"IOptionalFeatureDeviceErrorLockJammed": false,
|
||||
"IOptionalFeatureDeviceFrostProtectionError": true,
|
||||
"IOptionalFeatureDeviceInputLayoutMode": false,
|
||||
"IOptionalFeatureDeviceOperationMode": false,
|
||||
"IOptionalFeatureDeviceSwitchChannelMode": false,
|
||||
"IOptionalFeatureDeviceValveError": true,
|
||||
"IOptionalFeatureDeviceWaterError": true,
|
||||
"IOptionalFeatureDisplayContrast": false,
|
||||
"IOptionalFeatureDisplayMode": false,
|
||||
"IOptionalFeatureDutyCycle": true,
|
||||
"IOptionalFeatureInvertedDisplayColors": false,
|
||||
"IOptionalFeatureLowBat": true,
|
||||
"IOptionalFeatureMountingOrientation": false,
|
||||
"IOptionalFeatureOperationDays": false
|
||||
},
|
||||
"switchChannelMode": null,
|
||||
"temperatureHumiditySensorCommunicationError": null,
|
||||
"temperatureHumiditySensorError": null,
|
||||
"temperatureOutOfRange": false,
|
||||
"temperatureSensorError": null,
|
||||
"ticVersionError": null,
|
||||
"unreach": false,
|
||||
"valveFlowError": false,
|
||||
"valveWaterError": false
|
||||
},
|
||||
"1": {
|
||||
"channelRole": "WATERING_ACTUATOR",
|
||||
"deviceId": "3014F71100000000000SHWSM",
|
||||
"functionalChannelType": "WATERING_ACTUATOR_CHANNEL",
|
||||
"groupIndex": 1,
|
||||
"groups": ["00000000-0000-0000-0000-000000000023"],
|
||||
"index": 1,
|
||||
"label": "",
|
||||
"profileMode": "AUTOMATIC",
|
||||
"supportedOptionalFeatures": {
|
||||
"IFeatureWateringGroupActuatorChannel": true,
|
||||
"IFeatureWateringProfileActuatorChannel": true
|
||||
},
|
||||
"userDesiredProfileMode": "AUTOMATIC",
|
||||
"waterFlow": 12.0,
|
||||
"waterVolume": 455.0,
|
||||
"waterVolumeSinceOpen": 67.0,
|
||||
"wateringActive": false,
|
||||
"wateringOnTime": 3600.0
|
||||
}
|
||||
},
|
||||
"homeId": "00000000-0000-0000-0000-000000000001",
|
||||
"id": "3014F71100000000000SHWSM",
|
||||
"label": "Bewaesserungsaktor",
|
||||
"lastStatusUpdate": 1749501203047,
|
||||
"liveUpdateState": "LIVE_UPDATE_NOT_SUPPORTED",
|
||||
"manuallyUpdateForced": false,
|
||||
"manufacturerCode": 9,
|
||||
"measuredAttributes": {},
|
||||
"modelId": 586,
|
||||
"modelType": "ELV-SH-WSM",
|
||||
"oem": "eQ-3",
|
||||
"permanentlyReachable": true,
|
||||
"serializedGlobalTradeItemNumber": "3014F71100000000000SHWSM",
|
||||
"type": "WATERING_ACTUATOR",
|
||||
"updateState": "UP_TO_DATE"
|
||||
}
|
||||
},
|
||||
"groups": {
|
||||
|
@ -22,7 +22,7 @@ async def test_hmip_load_all_supported_devices(
|
||||
test_devices=None, test_groups=None
|
||||
)
|
||||
|
||||
assert len(mock_hap.hmip_device_by_entity_id) == 335
|
||||
assert len(mock_hap.hmip_device_by_entity_id) == 340
|
||||
|
||||
|
||||
async def test_hmip_remove_device(
|
||||
|
@ -35,6 +35,8 @@ from homeassistant.const import (
|
||||
UnitOfPower,
|
||||
UnitOfSpeed,
|
||||
UnitOfTemperature,
|
||||
UnitOfVolume,
|
||||
UnitOfVolumeFlowRate,
|
||||
)
|
||||
from homeassistant.core import HomeAssistant
|
||||
|
||||
@ -796,3 +798,66 @@ async def test_hmip_absolute_humidity_sensor_invalid_value(
|
||||
ha_state = hass.states.get(entity_id)
|
||||
|
||||
assert ha_state.state == STATE_UNKNOWN
|
||||
|
||||
|
||||
async def test_hmip_water_valve_current_water_flow(
|
||||
hass: HomeAssistant, default_mock_hap_factory: HomeFactory
|
||||
) -> None:
|
||||
"""Test HomematicipCurrentWaterFlow."""
|
||||
entity_id = "sensor.bewaesserungsaktor_currentwaterflow"
|
||||
entity_name = "Bewaesserungsaktor currentWaterFlow"
|
||||
device_model = "ELV-SH-WSM"
|
||||
mock_hap = await default_mock_hap_factory.async_get_mock_hap(
|
||||
test_devices=["Bewaesserungsaktor"]
|
||||
)
|
||||
|
||||
ha_state, hmip_device = get_and_check_entity_basics(
|
||||
hass, mock_hap, entity_id, entity_name, device_model
|
||||
)
|
||||
|
||||
assert ha_state.state == "12.0"
|
||||
assert (
|
||||
ha_state.attributes[ATTR_UNIT_OF_MEASUREMENT]
|
||||
== UnitOfVolumeFlowRate.LITERS_PER_MINUTE
|
||||
)
|
||||
assert ha_state.attributes[ATTR_STATE_CLASS] == SensorStateClass.MEASUREMENT
|
||||
|
||||
|
||||
async def test_hmip_water_valve_water_volume(
|
||||
hass: HomeAssistant, default_mock_hap_factory: HomeFactory
|
||||
) -> None:
|
||||
"""Test HomematicipWaterVolume."""
|
||||
entity_id = "sensor.bewaesserungsaktor_watervolume"
|
||||
entity_name = "Bewaesserungsaktor waterVolume"
|
||||
device_model = "ELV-SH-WSM"
|
||||
mock_hap = await default_mock_hap_factory.async_get_mock_hap(
|
||||
test_devices=["Bewaesserungsaktor"]
|
||||
)
|
||||
|
||||
ha_state, hmip_device = get_and_check_entity_basics(
|
||||
hass, mock_hap, entity_id, entity_name, device_model
|
||||
)
|
||||
|
||||
assert ha_state.state == "455.0"
|
||||
assert ha_state.attributes[ATTR_UNIT_OF_MEASUREMENT] == UnitOfVolume.LITERS
|
||||
assert ha_state.attributes[ATTR_STATE_CLASS] == SensorStateClass.TOTAL_INCREASING
|
||||
|
||||
|
||||
async def test_hmip_water_valve_water_volume_since_open(
|
||||
hass: HomeAssistant, default_mock_hap_factory: HomeFactory
|
||||
) -> None:
|
||||
"""Test HomematicipWaterVolumeSinceOpen."""
|
||||
entity_id = "sensor.bewaesserungsaktor_watervolumesinceopen"
|
||||
entity_name = "Bewaesserungsaktor waterVolumeSinceOpen"
|
||||
device_model = "ELV-SH-WSM"
|
||||
mock_hap = await default_mock_hap_factory.async_get_mock_hap(
|
||||
test_devices=["Bewaesserungsaktor"]
|
||||
)
|
||||
|
||||
ha_state, hmip_device = get_and_check_entity_basics(
|
||||
hass, mock_hap, entity_id, entity_name, device_model
|
||||
)
|
||||
|
||||
assert ha_state.state == "67.0"
|
||||
assert ha_state.attributes[ATTR_UNIT_OF_MEASUREMENT] == UnitOfVolume.LITERS
|
||||
assert ha_state.attributes[ATTR_STATE_CLASS] == SensorStateClass.TOTAL_INCREASING
|
||||
|
35
tests/components/homematicip_cloud/test_valve.py
Normal file
35
tests/components/homematicip_cloud/test_valve.py
Normal file
@ -0,0 +1,35 @@
|
||||
"""Test HomematicIP Cloud valve entities."""
|
||||
|
||||
from homeassistant.components.valve import SERVICE_OPEN_VALVE, ValveState
|
||||
from homeassistant.const import Platform
|
||||
from homeassistant.core import HomeAssistant
|
||||
|
||||
from .helper import HomeFactory, async_manipulate_test_data, get_and_check_entity_basics
|
||||
|
||||
|
||||
async def test_watering_valve(
|
||||
hass: HomeAssistant, default_mock_hap_factory: HomeFactory
|
||||
) -> None:
|
||||
"""Test HomematicIP watering valve."""
|
||||
entity_id = "valve.bewaesserungsaktor_watering"
|
||||
entity_name = "Bewaesserungsaktor watering"
|
||||
device_model = "ELV-SH-WSM"
|
||||
mock_hap = await default_mock_hap_factory.async_get_mock_hap(
|
||||
test_devices=["Bewaesserungsaktor"]
|
||||
)
|
||||
|
||||
ha_state, hmip_device = get_and_check_entity_basics(
|
||||
hass, mock_hap, entity_id, entity_name, device_model
|
||||
)
|
||||
|
||||
assert ha_state.state == ValveState.CLOSED
|
||||
|
||||
await hass.services.async_call(
|
||||
Platform.VALVE, SERVICE_OPEN_VALVE, {"entity_id": entity_id}, blocking=True
|
||||
)
|
||||
|
||||
await async_manipulate_test_data(
|
||||
hass, hmip_device, "wateringActive", True, channel=1
|
||||
)
|
||||
ha_state = hass.states.get(entity_id)
|
||||
assert ha_state.state == ValveState.OPEN
|
Loading…
x
Reference in New Issue
Block a user