Repository: zigpy/zha-device-handlers

Issue:
Add support for LUMI lumi.plug.maus01
# Node Descriptor
```
<Optional byte1=1 byte2=64 mac_capability_flags=142 manufacturer_code=4447 maximum_buffer_size=127 maximum_incoming_transfer_size=100 server_mask=0 maximum_outgoing_transfer_size=100 descriptor_capability_field=0>
```

# Discovery Information

```
Discovered endpoints: [1, 2, 3, 100]
Discovered endpoint information: <SimpleDescriptor endpoint=1 profile=260 device_type=81 device_version=1 input_clusters=[0, 4, 3, 6, 16, 5, 10, 1, 2, 2820] output_clusters=[25, 10]>

Manufacturer: LUMI
Model: lumi.plug.maus01

Discovered endpoint information: <SimpleDescriptor endpoint=2 profile=260 device_type=9 device_version=1 input_clusters=[12] output_clusters=[12, 4]>

Discovered endpoint information: <SimpleDescriptor endpoint=3 profile=260 device_type=83 device_version=1 input_clusters=[12] output_clusters=[12]>

Discovered endpoint information: <SimpleDescriptor endpoint=100 profile=260 device_type=263 device_version=2 input_clusters=[15] output_clusters=[15, 4]>
Checking quirks for LUMI lumi.plug.maus01
```


Discussion:

Resolving patch:
diff --git a/zhaquirks/xiaomi/__init__.py b/zhaquirks/xiaomi/__init__.py
--- a/zhaquirks/xiaomi/__init__.py
+++ b/zhaquirks/xiaomi/__init__.py
@@ -6,6 +6,7 @@
 from zigpy import types as t
 from zigpy.quirks import CustomCluster, CustomDevice
 from zigpy.zcl.clusters.general import Basic, PowerConfiguration
+from zigpy.zcl.clusters.homeautomation import ElectricalMeasurement
 from zigpy.zcl.clusters.measurement import (
     OccupancySensing,
     PressureMeasurement,
@@ -25,8 +26,8 @@
     MOTION_EVENT,
     OFF,
     ON,
-    VALUE,
     UNKNOWN,
+    VALUE,
     ZHA_SEND_EVENT,
     ZONE_STATE,
 )
@@ -43,12 +44,18 @@
 MOTION_TYPE = 0x000D
 OCCUPANCY_STATE = 0
 PATH = "path"
+POWER = "power"
+CONSUMPTION = "consumption"
+VOLTAGE = "voltage"
 PRESSURE_MEASUREMENT = "pressure_measurement"
 PRESSURE_REPORTED = "pressure_reported"
 STATE = "state"
 TEMPERATURE = "temperature"
 TEMPERATURE_MEASUREMENT = "temperature_measurement"
 TEMPERATURE_REPORTED = "temperature_reported"
+POWER_REPORTED = "power_reported"
+CONSUMPTION_REPORTED = "consumption_reported"
+VOLTAGE_REPORTED = "voltage_reported"
 XIAOMI_AQARA_ATTRIBUTE = 0xFF01
 XIAOMI_ATTR_3 = "X-attrib-3"
 XIAOMI_ATTR_4 = "X-attrib-4"
@@ -163,6 +170,18 @@ def _update_attribute(self, attrid, value):
             self.endpoint.device.pressure_bus.listener_event(
                 PRESSURE_REPORTED, attributes[PRESSURE_MEASUREMENT] / 100
             )
+        if POWER in attributes:
+            self.endpoint.device.power_bus.listener_event(
+                POWER_REPORTED, attributes[POWER]
+            )
+        if CONSUMPTION in attributes:
+            self.endpoint.device.consumption_bus.listener_event(
+                CONSUMPTION_REPORTED, attributes[CONSUMPTION]
+            )
+        if VOLTAGE in attributes:
+            self.endpoint.device.voltage_bus.listener_event(
+                VOLTAGE_REPORTED, attributes[VOLTAGE] * 0.1
+            )
 
     def _parse_aqara_attributes(self, value):
         """Parse non standard atrributes."""
@@ -190,6 +209,10 @@ def _parse_aqara_attributes(self, value):
                     102: PRESSURE_MEASUREMENT,
                 }
             )
+        elif (
+            MODEL in self._attr_cache and self._attr_cache[MODEL] == "lumi.plug.maus01"
+        ):
+            attribute_names.update({149: CONSUMPTION, 150: VOLTAGE, 152: POWER})
 
         result = {}
         while value:
@@ -385,3 +408,37 @@ def __init__(self, *args, **kwargs):
     def pressure_reported(self, value):
         """Pressure reported."""
         self._update_attribute(self.ATTR_ID, value)
+
+
+class ElectricalMeasurementCluster(CustomCluster, ElectricalMeasurement):
+    """Electrical measurement cluster to receive reports that are sent to the basic cluster."""
+
+    cluster_id = ElectricalMeasurement.cluster_id
+    POWER_ID = 0x050B
+    VOLTAGE_ID = 0x0500
+    CONSUMPTION_ID = 0x0304
+
+    def __init__(self, *args, **kwargs):
+        """Init."""
+        super().__init__(*args, **kwargs)
+        self.endpoint.device.voltage_bus.add_listener(self)
+        self.endpoint.device.consumption_bus.add_listener(self)
+        self.endpoint.device.power_bus.add_listener(self)
+
+    def power_reported(self, value):
+        """Power reported."""
+        self._update_attribute(self.POWER_ID, value)
+
+    def voltage_reported(self, value):
+        """Voltage reported."""
+        self._update_attribute(self.VOLTAGE_ID, value)
+
+    def consumption_reported(self, value):
+        """Consumption reported."""
+        self._update_attribute(self.CONSUMPTION_ID, value)
+
+    async def read_attributes_raw(self, attributes, manufacturer=None):
+        """Prevent remote reads."""
+        attributes = [t.uint16_t(a) for a in attributes]
+        values = [self._attr_cache.get(attr) for attr in attributes]
+        return values
diff --git a/zhaquirks/xiaomi/aqara/plug.py b/zhaquirks/xiaomi/aqara/plug.py
new file mode 100644
--- /dev/null
+++ b/zhaquirks/xiaomi/aqara/plug.py
@@ -0,0 +1,161 @@
+"""Xiaomi aqara button sensor."""
+import logging
+
+from zigpy.profiles import zha
+from zigpy.zcl.clusters.general import (
+    AnalogInput,
+    Basic,
+    BinaryInput,
+    BinaryOutput,
+    DeviceTemperature,
+    Groups,
+    Identify,
+    OnOff,
+    Ota,
+    PowerConfiguration,
+    Scenes,
+    Time,
+)
+from zigpy.zcl.clusters.homeautomation import ElectricalMeasurement
+
+from ... import Bus, CustomCluster
+from ...const import (
+    DEVICE_TYPE,
+    ENDPOINTS,
+    INPUT_CLUSTERS,
+    MODELS_INFO,
+    OUTPUT_CLUSTERS,
+    PROFILE_ID,
+)
+from .. import (
+    LUMI,
+    POWER_REPORTED,
+    BasicCluster,
+    ElectricalMeasurementCluster,
+    XiaomiCustomDevice,
+)
+
+_LOGGER = logging.getLogger(__name__)
+
+
+class AnalogInputCluster(CustomCluster, AnalogInput):
+    """Analog input cluster."""
+
+    cluster_id = AnalogInput.cluster_id
+
+    def __init__(self, *args, **kwargs):
+        """Init."""
+        self._current_state = {}
+        super().__init__(*args, **kwargs)
+
+    def _update_attribute(self, attrid, value):
+        super()._update_attribute(attrid, value)
+        if value is not None and value >= 0:
+            self.endpoint.device.power_bus.listener_event(POWER_REPORTED, value)
+
+
+class Plug(XiaomiCustomDevice):
+    """lumi.plug.maus01 device."""
+
+    def __init__(self, *args, **kwargs):
+        """Init."""
+        self.voltage_bus = Bus()
+        self.consumption_bus = Bus()
+        self.power_bus = Bus()
+        super().__init__(*args, **kwargs)
+
+    signature = {
+        MODELS_INFO: [(LUMI, "lumi.plug.maus01")],
+        ENDPOINTS: {
+            # <SimpleDescriptor endpoint=1 profile=260 device_type=81
+            # device_version=1
+            # input_clusters=[0, 4, 3, 6, 16, 5, 10, 1, 2, 2820]
+            # output_clusters=[25, 10]>
+            1: {
+                PROFILE_ID: zha.PROFILE_ID,
+                DEVICE_TYPE: zha.DeviceType.SMART_PLUG,
+                INPUT_CLUSTERS: [
+                    Basic.cluster_id,
+                    PowerConfiguration.cluster_id,
+                    DeviceTemperature.cluster_id,
+                    Groups.cluster_id,
+                    Identify.cluster_id,
+                    OnOff.cluster_id,
+                    Scenes.cluster_id,
+                    BinaryOutput.cluster_id,
+                    Time.cluster_id,
+                    ElectricalMeasurement.cluster_id,
+                ],
+                OUTPUT_CLUSTERS: [Ota.cluster_id, Time.cluster_id],
+            },
+            # <SimpleDescriptor endpoint=2 profile=260 device_type=9
+            # device_version=1
+            # input_clusters=[12]
+            # output_clusters=[12, 4]>
+            2: {
+                PROFILE_ID: zha.PROFILE_ID,
+                DEVICE_TYPE: zha.DeviceType.MAIN_POWER_OUTLET,
+                INPUT_CLUSTERS: [AnalogInput.cluster_id],
+                OUTPUT_CLUSTERS: [AnalogInput.cluster_id, Groups.cluster_id],
+            },
+            # <SimpleDescriptor endpoint=3 profile=260 device_type=83
+            # device_version=1
+            # input_clusters=[12]
+            # output_clusters=[12]>
+            3: {
+                PROFILE_ID: zha.PROFILE_ID,
+                DEVICE_TYPE: zha.DeviceType.METER_INTERFACE,
+                INPUT_CLUSTERS: [AnalogInput.cluster_id],
+                OUTPUT_CLUSTERS: [AnalogInput.cluster_id],
+            },
+            # <SimpleDescriptor endpoint=100 profile=260 device_type=263
+            # device_version=2
+            # input_clusters=[15]
+            # output_clusters=[15, 4]>
+            100: {
+                PROFILE_ID: zha.PROFILE_ID,
+                DEVICE_TYPE: zha.DeviceType.OCCUPANCY_SENSOR,
+                INPUT_CLUSTERS: [BinaryInput.cluster_id],
+                OUTPUT_CLUSTERS: [BinaryInput.cluster_id, Groups.cluster_id],
+            },
+        },
+    }
+    replacement = {
+        ENDPOINTS: {
+            1: {
+                PROFILE_ID: zha.PROFILE_ID,
+                DEVICE_TYPE: zha.DeviceType.SMART_PLUG,
+                INPUT_CLUSTERS: [
+                    BasicCluster,
+                    PowerConfiguration.cluster_id,
+                    DeviceTemperature.cluster_id,
+                    Groups.cluster_id,
+                    Identify.cluster_id,
+                    OnOff.cluster_id,
+                    Scenes.cluster_id,
+                    BinaryOutput.cluster_id,
+                    Time.cluster_id,
+                    ElectricalMeasurementCluster,
+                ],
+                OUTPUT_CLUSTERS: [Ota.cluster_id, Time.cluster_id],
+            },
+            2: {
+                PROFILE_ID: zha.PROFILE_ID,
+                DEVICE_TYPE: zha.DeviceType.MAIN_POWER_OUTLET,
+                INPUT_CLUSTERS: [AnalogInputCluster],
+                OUTPUT_CLUSTERS: [AnalogInput.cluster_id, Groups.cluster_id],
+            },
+            3: {
+                PROFILE_ID: zha.PROFILE_ID,
+                DEVICE_TYPE: zha.DeviceType.METER_INTERFACE,
+                INPUT_CLUSTERS: [AnalogInput.cluster_id],
+                OUTPUT_CLUSTERS: [AnalogInput.cluster_id],
+            },
+            100: {
+                PROFILE_ID: zha.PROFILE_ID,
+                DEVICE_TYPE: zha.DeviceType.OCCUPANCY_SENSOR,
+                INPUT_CLUSTERS: [BinaryInput.cluster_id],
+                OUTPUT_CLUSTERS: [BinaryInput.cluster_id, Groups.cluster_id],
+            },
+        }
+    }


Test patch: