Example #1
0
async def test_zigpy_request(application, mocker):
    app, znp_server = application
    await app.startup(auto_form=False)

    TSN = 1

    device = app.add_device(ieee=t.EUI64(range(8)), nwk=0xAABB)
    device.status = zigpy.device.Status.ENDPOINTS_INIT
    device.initializing = False

    device.add_endpoint(1).add_input_cluster(6)

    # Respond to a light turn on request
    data_req = znp_server.reply_once_to(
        request=c.AF.DataRequestExt.Req(
            DstAddrModeAddress=t.AddrModeAddress(mode=t.AddrMode.NWK,
                                                 address=device.nwk),
            DstEndpoint=1,
            SrcEndpoint=1,
            ClusterId=6,
            TSN=TSN,
            Data=bytes([0x01, TSN, 0x01]),
            partial=True,
        ),
        responses=[
            c.AF.DataRequestExt.Rsp(Status=t.Status.SUCCESS),
            c.AF.DataConfirm.Callback(
                Status=t.Status.SUCCESS,
                Endpoint=1,
                TSN=TSN,
            ),
            c.ZDO.SrcRtgInd.Callback(DstAddr=device.nwk, Relays=[]),
            c.AF.IncomingMsg.Callback(
                GroupId=0x0000,
                ClusterId=6,
                SrcAddr=device.nwk,
                SrcEndpoint=1,
                DstEndpoint=1,
                WasBroadcast=False,
                LQI=63,
                SecurityUse=False,
                TimeStamp=1198515,
                TSN=0,
                Data=bytes([0x08, TSN, 0x0B, 0x00, 0x00]),
                MacSrcAddr=device.nwk,
                MsgResultRadius=29,
            ),
        ],
    )

    # Turn on the light
    await device.endpoints[1].on_off.on()
    await data_req
Example #2
0
async def test_zigpy_request_failure(application, mocker):
    app, znp_server = application
    await app.startup(auto_form=False)

    TSN = 1

    device = app.add_device(ieee=t.EUI64(range(8)), nwk=0xAABB)
    device.status = zigpy.device.Status.ENDPOINTS_INIT
    device.initializing = False

    device.add_endpoint(1).add_input_cluster(6)

    # Fail to respond to a light turn on request
    znp_server.reply_to(
        request=c.AF.DataRequestExt.Req(
            DstAddrModeAddress=t.AddrModeAddress(mode=t.AddrMode.NWK,
                                                 address=device.nwk),
            DstEndpoint=1,
            SrcEndpoint=1,
            ClusterId=6,
            TSN=TSN,
            Data=bytes([0x01, TSN, 0x01]),
            partial=True,
        ),
        responses=[
            c.AF.DataRequestExt.Rsp(Status=t.Status.SUCCESS),
            c.AF.DataConfirm.Callback(
                Status=t.Status.FAILURE,
                Endpoint=1,
                TSN=TSN,
            ),
        ],
    )

    mocker.patch.object(app,
                        "_send_request",
                        new=CoroutineMock(wraps=app._send_request))

    # Fail to turn on the light
    with pytest.raises(zigpy.exceptions.DeliveryError):
        await device.endpoints[1].on_off.on()

    assert app._send_request.call_count == 1
Example #3
0
def generate_device(manufacturer: str, model: str) -> Optional[Device]:
    """Generate device from quirks. Should be called earlier:
        zhaquirks.setup()

    Or direct import:
        from zhaquirks.xiaomi.mija.sensor_switch import MijaButton

    Used like a Cluster:
        hdr, value = device.deserialize(<endpoint_id>, <cluster_id>, data)
    """
    quirks = zigpy.quirks.get_quirk_list(manufacturer, model)
    if not quirks:
        return None

    device = Device(None, None, 0)
    device.manufacturer = manufacturer
    device.model = model

    quirk: zigpy.quirks.CustomDevice = quirks[0]
    if SIG_ENDPOINTS in quirk.replacement:
        for endpoint_id in quirk.replacement[SIG_ENDPOINTS].keys():
            device.add_endpoint(endpoint_id)

    return quirks[0](None, None, 0, device)
Example #4
0
    def _mock_dev(
        endpoints,
        ieee="00:0d:6f:00:0a:90:69:e7",
        manufacturer="FakeManufacturer",
        model="FakeModel",
        node_descriptor=b"\x02@\x807\x10\x7fd\x00\x00*d\x00\x00",
        nwk=0xB79C,
        patch_cluster=True,
        quirk=None,
    ):
        """Make a fake device using the specified cluster classes."""
        device = zigpy.device.Device(zigpy_app_controller,
                                     zigpy.types.EUI64.convert(ieee), nwk)
        device.manufacturer = manufacturer
        device.model = model
        device.node_desc = zdo_t.NodeDescriptor.deserialize(node_descriptor)[0]
        device.last_seen = time.time()

        for epid, ep in endpoints.items():
            endpoint = device.add_endpoint(epid)
            endpoint.device_type = ep[SIG_EP_TYPE]
            endpoint.profile_id = ep.get(SIG_EP_PROFILE)
            endpoint.request = AsyncMock(return_value=[0])

            for cluster_id in ep.get(SIG_EP_INPUT, []):
                endpoint.add_input_cluster(cluster_id)

            for cluster_id in ep.get(SIG_EP_OUTPUT, []):
                endpoint.add_output_cluster(cluster_id)

        if quirk:
            device = quirk(zigpy_app_controller, device.ieee, device.nwk,
                           device)

        if patch_cluster:
            for endpoint in (ep for epid, ep in device.endpoints.items()
                             if epid):
                endpoint.request = AsyncMock(return_value=[0])
                for cluster in itertools.chain(endpoint.in_clusters.values(),
                                               endpoint.out_clusters.values()):
                    common.patch_cluster(cluster)

        return device
Example #5
0
    def from_signature(
        cls, device: zigpy.device.Device, model: Optional[str] = None
    ) -> zigpy.device.Device:
        """Update device accordingly to quirk signature."""

        assert isinstance(cls.signature, dict)
        if model is None:
            model = cls.signature[MODEL]
        manufacturer = cls.signature.get(MANUFACTURER)
        if manufacturer is None:
            manufacturer = cls.signature[MODELS_INFO][0][0]

        device.node_desc = cls.signature[NODE_DESCRIPTOR]

        endpoints = cls.signature[ENDPOINTS]
        for ep_id, ep_data in endpoints.items():
            endpoint = device.add_endpoint(ep_id)
            endpoint.profile_id = ep_data[PROFILE_ID]
            endpoint.device_type = ep_data[DEVICE_TYPE]
            for cluster_id in ep_data[INPUT_CLUSTERS]:
                cluster = endpoint.add_input_cluster(cluster_id)
                if cluster.ep_attribute == "basic":
                    manuf_attr_id = cluster.attridx[MANUFACTURER]
                    cluster._update_attribute(  # pylint: disable=W0212
                        manuf_attr_id, manufacturer
                    )
                    cluster._update_attribute(  # pylint: disable=W0212
                        cluster.attridx[MODEL], model
                    )
            for cluster_id in ep_data[OUTPUT_CLUSTERS]:
                endpoint.add_output_cluster(cluster_id)
            endpoint.status = zigpy.endpoint.Status.ZDO_INIT

        device.status = zigpy.device.Status.ENDPOINTS_INIT
        device.manufacturer = manufacturer
        device.model = model

        return device
Example #6
0
    def handle_zcl(self, dataPayload: ZclDataPayload):
        # LOGGER.debug("Received ZCL data %s", dataPayload)

        # if (dataPayload.frame && dataPayload.frame.Cluster.name === 'touchlink') {
        #    // This is handled by touchlink
        #     return
        # }

        nwk = dataPayload.address
        try:
            device: zigpy.device.Device = self.get_device(nwk=nwk)
        except KeyError:
            LOGGER.debug("Received ZCL frame from unknown device: %s", nwk)
            return

        # device.updateLastSeen()

        # endpoint: zigpy.endpoint.Endpoint
        #
        try:
            endpoint = device.endpoints[dataPayload.endpoint]
        except KeyError:
            LOGGER.debug("ZCL data is from unknown endpoint %d from device %s, creating it...", dataPayload.endpoint,
                         nwk)
            endpoint = device.add_endpoint(dataPayload.endpoint)

        # cluster = Cluster.from_id(endpoint, dataPayload.clusterid)
        # hdr, data = cluster.deserialize(dataPayload.data)
        # LOGGER.info("ZLC: %s %s", hdr.command_id, data)
        # LOGGER.debug("ZLC: %s", hdr)

        profile = 0
        device.handle_message(
            profile,
            dataPayload.clusterid,
            dataPayload.endpoint,
            dataPayload.dstendpoint,
            dataPayload.data,
        )
        # endpoint.handle_message(profile, dataPayload.clusterid, hdr, data)

        # if hdr.frame_control.is_general():
        #     if hdr.command_id == Command.Report_Attributes:
        #         pass
        # else:
        #     pass
        #
        # if hdr.command_id == Command.Read_Attributes_rsp or hdr.command_id == Command.Report_Attributes:
        #     # Some device report, e.g. it's modelID through a readResponse or attributeReport
        #     for key, value in data.items():
        #         property = cluster.discover_attributes.ReportablePropertiesMapping[key]
        #         if property && !device[property.key]:
        #             property.set(value, device)
        #
        #     endpoint.saveClusterAttributeKeyValue(clusterName, data)

        '''
        // Parse command for event
        let type: Events.MessagePayloadType = undefined
        let data: KeyValue
        let clusterName = undefined
        const meta: {zclTransactionSequenceNumber?: number} = {}

        const frame = dataPayload.frame
        const command = frame.getCommand()
        clusterName = frame.Cluster.name
        meta.zclTransactionSequenceNumber = frame.Header.transactionSequenceNumber

        if (frame.isGlobal()) {
            if (frame.isCommand('report')) {
                type = 'attributeReport'
                data = ZclFrameConverter.attributeKeyValue(dataPayload.frame)
            } else if (frame.isCommand('read')) {
                type = 'read'
                data = ZclFrameConverter.attributeList(dataPayload.frame)
            } else if (frame.isCommand('write')) {
                type = 'write'
                data = ZclFrameConverter.attributeKeyValue(dataPayload.frame)
            } else {
                /* istanbul ignore else */
                if (frame.isCommand('readRsp')) {
                    type = 'readResponse'
                    data = ZclFrameConverter.attributeKeyValue(dataPayload.frame)
                }
            }
        } else {
            /* istanbul ignore else */
            if (frame.isSpecific()) {
                if (Events.CommandsLookup[command.name]) {
                    type = Events.CommandsLookup[command.name]
                    data = dataPayload.frame.Payload
                } else {
                    debug.log("Skipping command '${command.name}' because it is missing from the lookup")
                }
            }
        }

        if (type === 'readResponse' || type === 'attributeReport') {
            // Some device report, e.g. it's modelID through a readResponse or attributeReport
            for (const [key, value] of Object.entries(data)) {
                const property =  Device.ReportablePropertiesMapping[key]
                if (property && !device[property.key]) {
                    property.set(value, device)
                }
            }

            endpoint.saveClusterAttributeKeyValue(clusterName, data)
        }


        if (type && data) {
            const endpoint = device.getEndpoint(dataPayload.endpoint)
            const linkquality = dataPayload.linkquality
            const groupID = dataPayload.groupID
            const eventData: Events.MessagePayload = {
                type: type, device, endpoint, data, linkquality, groupID, cluster: clusterName, meta
            }

            this.emit(Events.Events.message, eventData)
        }


        const frame = dataPayload.frame

        // Send a default response if necessary.
        if (!frame.Header.frameControl.disableDefaultResponse) {
            try {
                await endpoint.defaultResponse(
                    frame.getCommand().ID, 0, frame.Cluster.ID, frame.Header.transactionSequenceNumber,
                )
            } catch (error) {
                debug.error("Default response to ${device.ieeeAddr} failed")
            }
        }

        // Reponse to time reads
        if (frame.isGlobal() && frame.isCluster('genTime') && frame.isCommand('read')) {
            const time = Math.round(((new Date()).getTime() - OneJanuary2000) / 1000)
            try {
                await endpoint.readResponse(frame.Cluster.ID, frame.Header.transactionSequenceNumber, {time})
            } catch (error) {
                debug.error("genTime response to ${device.ieeeAddr} failed")
            }
        }
        '''
        pass