zwave-js / certification-backlog

Issues and discussions related to Z-Wave certification of HA + Z-Wave JS
1 stars 0 forks source link

Z-Wave JS: Certification requirements for Meter CC not fulfilled #17

Open marcelveldt opened 1 year ago

marcelveldt commented 1 year ago

From core created by AlCalzone: home-assistant/core#92799

The problem

If a device reports support for resetting meters, the end user must be able to perform this action. The driver exposes a writeonly value in this case, see node diagnostics.

We should add additional button entities, similar to PING or the planned IDENTIFY (Indicator CC).

Diagnostics information

zwave_js-f0926e113c178046d0d249fcdc42964a-Node 58-24fb445b526b3d5d614f25141d6cc278.json.txt

marcelveldt commented 1 year ago

We need to add additional button entities to implement this

raman325 commented 8 months ago

is the zwave_js.reset_meter service not sufficient for this?

And how does a device advertise support for reset?

AlCalzone commented 7 months ago

It's enough to pass the test (well, except for a bug in the test suite 🙄), but having buttons could be nice. Not sure.

marcelveldt commented 7 months ago

For certification there is nothing left to do, we have the service call. Nice to have would be a button entity to reset the meter value(s)

raman325 commented 7 months ago

Can we close this and add a nice to have issue? We can link to this one I just want to make the scope clear as I triage the board

marcelveldt commented 7 months ago

done

raman325 commented 7 months ago

sorry, this is what I meant ^

AlCalzone commented 2 months ago

Tested again, looks like the service call isn't working as expected. https://github.com/home-assistant/core/issues/116222

I also found some discrepancies from the specs, which mean the reset calls will need an overhaul anyways: https://github.com/zwave-js/node-zwave-js/pull/6921

AlCalzone commented 1 month ago

This mock-server script can be used to test the implementation. It simulates a node with 4 meter readings, two of which are accumulated and can be reset. Every 5 seconds, a random reading increases by a small amount.

To test, ensure that a reset all only results in a single call to Z-Wave JS and a single MeterCCReset being sent.

// @ts-check
const { CommandClasses } = require("@zwave-js/core");
const { ccCaps, createMockZWaveRequestFrame } = require("@zwave-js/testing");
const { roundTo } = require("alcalzone-shared/math");
const { RateType, MeterCCReport } = require("zwave-js");

let simulationTimer;
process.on("SIGINT", () => {
    if (simulationTimer) clearInterval(simulationTimer);
});

const values = new Map([
    [0x00, 1],
    [0x03, 2.5],
    [0x04, 4],
    [0x05, 50],
]);

/** @type {import("zwave-js/Testing").MockServerOptions["config"]} */
module.exports.default = {
    nodes: [
        {
            id: 2,
            capabilities: {
                commandClasses: [
                    CommandClasses.Version,
                    ccCaps({
                        ccId: CommandClasses.Meter,
                        isSupported: true,
                        version: 6,
                        meterType: 0x01, // Electric
                        supportedScales: [0x00, 0x03, 0x04, 0x05],
                        supportedRateTypes: [RateType.Consumed],
                        supportsReset: true,
                        getValue: (scale) => {
                            return values.get(scale) ?? 0;
                        },
                        onReset: (options) => {
                            if (!options) {
                                values.set(0x00, 0);
                                values.set(0x03, 0);
                            } else {
                                const { scale, targetValue } = options;
                                values.set(scale, targetValue);
                            }
                        },
                    }),
                ],
            },
            behaviors: [
                {
                    // Small hack - start the state simulation timer when the node
                    // receives a command from the controller
                    async onControllerFrame(controller, self, _frame) {
                        if (!simulationTimer) {
                            // Then send notifications regularly
                            simulationTimer = setInterval(() => {
                                // Randomly select a scale
                                const scale = Array.from(values.keys())[
                                    Math.floor(Math.random() * values.size)
                                ];
                                // Randomly increase the value by a small amount
                                const newValue = (values.get(scale) ?? 0)
                                    + roundTo(
                                        Math.random() * 5,
                                        1,
                                    );
                                values.set(scale, newValue);

                                const cc = new MeterCCReport(self.host, {
                                    nodeId: controller.host.ownNodeId,
                                    type: 0x01, // Electric
                                    scale,
                                    rateType: RateType.Consumed,
                                    value: newValue,
                                });

                                self.sendToController(
                                    createMockZWaveRequestFrame(cc, {
                                        ackRequested: false,
                                    }),
                                );
                                // Tune this value according to how often you want to get notifications
                            }, 10000).unref();
                        }
                        return false;
                    },
                },
            ],
        },
    ],
};