OpenZWave / open-zwave

a C++ library to control Z-Wave Networks via a USB Z-Wave Controller.
http://www.openzwave.net/
GNU Lesser General Public License v3.0
1.05k stars 912 forks source link

This xml file of the zwave classes may be of use. #1824

Closed kdschlosser closed 5 years ago

kdschlosser commented 5 years ago

I was just poking about the SilLabs website and I stumbled upon an xml file that is a representation of all of the classes that exist in the zwave specification. this includes notifications, command classes, generic types, specific types, all of the indexes for the values along with labels data types hash codes. masks (if used)

It seems to be complete. I did not know if anyone has seen this before. I thought it may be of use since a very large portion of the openzwave library relies on xml. specifically for the ease of updating.

Here would be a use case example. A new version of a command class gets released. and that new version adds support for a new device type. Say another thermostat setpoint. Since the core code is only limited by the constants that are hard coded into it. this would allow for an update via an xml file and not have to do a new release.

@Fishwaldo I think this might help with the value indexes.

If you have not seen this file before I am sure I can locate it again if you want a link to where it is on Sil's website. Let me know.

GitBug (intentional) would not allow me to upload an xml file. the xml file is in the zip. zwave_classes.zip

Fishwaldo commented 5 years ago

Already in OZW: https://github.com/OpenZWave/open-zwave/blob/master/config/device_classes.xml

kdschlosser commented 5 years ago

OHHHH no this is not that file.

Not the same thing. far from it. This is a class layout of the entire zwave specification.

kdschlosser commented 5 years ago

Here is a header file that was included with the xml file.

ZW_classcmd.zip

Fishwaldo commented 5 years ago

We work off the CommandClass specification document(s) as that will be more explicit in terms of the changes/constraints with new versions.

I should also point out that the Value Indexes don’t directly map to anything in the Specifications. Often a single Z-Wave packet can contain multiple Values that we map to Values in code (and in some cases values become redundant or useless with newer versions of the commandclass as is the case with things like Notification CC.

kdschlosser commented 5 years ago

they can be made to map since the use of indexes is kind of sort of new. here is n example with COMMAND_CLASS_WAKE_UP

this is what is outlined in the xml

<cmd_class key="0x84" version="2" name="COMMAND_CLASS_WAKE_UP" help="Command Class Wake Up" read_only="false" comment="">
    <cmd key="0x09" name="WAKE_UP_INTERVAL_CAPABILITIES_GET" help="Wake Up Interval Capabilities Get" comment="" />
    <cmd key="0x0A" name="WAKE_UP_INTERVAL_CAPABILITIES_REPORT" help="Wake Up Interval Capabilities Report" comment="">
      <param key="0x00" name="Minimum Wake Up Interval Seconds" type="BIT_24" typehashcode="0x04" comment="">
        <bit_24 key="0x00" hasdefines="false" showhex="false" />
      </param>
      <param key="0x01" name="Maximum Wake Up Interval Seconds" type="BIT_24" typehashcode="0x04" comment="">
        <bit_24 key="0x00" hasdefines="false" showhex="false" />
      </param>
      <param key="0x02" name="Default Wake Up Interval Seconds" type="BIT_24" typehashcode="0x04" comment="">
        <bit_24 key="0x00" hasdefines="false" showhex="false" />
      </param>
      <param key="0x03" name="Wake Up Interval Step Seconds" type="BIT_24" typehashcode="0x04" comment="">
        <bit_24 key="0x00" hasdefines="false" showhex="false" />
      </param>
    </cmd>
    <cmd key="0x05" name="WAKE_UP_INTERVAL_GET" help="Wake Up Interval Get" comment="" />
    <cmd key="0x06" name="WAKE_UP_INTERVAL_REPORT" help="Wake Up Interval Report" comment="">
      <param key="0x00" name="Seconds" type="BIT_24" typehashcode="0x04" comment="">
        <bit_24 key="0x00" hasdefines="false" showhex="false" />
      </param>
      <param key="0x01" name="NodeID" type="BYTE" typehashcode="0x01" comment="" encaptype="NODE_NUMBER">
        <valueattrib key="0x00" hasdefines="false" showhex="false" />
      </param>
    </cmd>
    <cmd key="0x04" name="WAKE_UP_INTERVAL_SET" help="Wake Up Interval Set" comment="">
      <param key="0x00" name="Seconds" type="BIT_24" typehashcode="0x04" comment="">
        <bit_24 key="0x00" hasdefines="false" showhex="false" />
      </param>
      <param key="0x01" name="NodeID" type="BYTE" typehashcode="0x01" comment="" encaptype="NODE_NUMBER">
        <valueattrib key="0x00" hasdefines="false" showhex="false" />
      </param>
    </cmd>
    <cmd key="0x08" name="WAKE_UP_NO_MORE_INFORMATION" help="Wake Up No More Information" comment="" />
    <cmd key="0x07" name="WAKE_UP_NOTIFICATION" help="Wake Up Notification" comment="" />
  </cmd_class>

specifically the WAKE_UP_INTERVAL_CAPABILITIES_REPORT look at the child nodes and the param key attribute.

and this is what you have coded.

struct ValueID_Index_WakeUp {
    enum _enumerated {
        Interval = 0,
        Min_Interval = 1,
        Max_Interval = 2,
        Default_Interval = 3,
        Interval_Step = 4
    };
    _enumerated _value;

    ValueID_Index_WakeUp(_enumerated value) : _value(value) {}

    operator _enumerated() const { return _value; }

    const char *_to_string() const {
        for (size_t index = 0; index < _count; ++index) {
            if (_values()[index] == _value)return _names()[index];
        }
        return NULL;
    }

    static const size_t _count = 5;

    static const int *_values() {
        static const int values[] = {
                (ignore_assign) Interval = 0,
                (ignore_assign) Min_Interval = 1,
                (ignore_assign) Max_Interval = 2,
                (ignore_assign) Default_Interval = 3,
                (ignore_assign) Interval_Step = 4,
        };
        return values;
    }

    static const char *const *_names() {
        static const char *const raw_names[] = {
                "Interval = 0",
                "Min_Interval = 1",
                "Max_Interval = 2",
                "Default_Interval = 3",
                "Interval_Step = 4",
        };
        static char *processed_names[_count];
        static bool initialized = false;
        if (!initialized) {
            for (size_t index = 0; index < _count; ++index) {
                size_t length = std::strcspn(raw_names[index],
                                             " =\t\n\r");
                processed_names[index] = new char[length + 1];
                strncpy(processed_names[index],
                        raw_names[index],
                        length);
                processed_names[index][length] = '\0';
            }
        }
        return processed_names;
    }
};;

they are extremely close. I am not sure exactly what the value you have set to index 0 is. for. it states Interval = 0. that is the only difference.

from a quick skim over the xml there are a lot of them that are correct.

and also You will see in the xml things like this

<cmd key="0x12" name="VERSION_REPORT" help="Version Report" comment="">
      <param key="0x00" name="Z-Wave Library Type" type="BYTE" typehashcode="0x01" comment="">
        <valueattrib key="0x00" hasdefines="false" showhex="true" />
      </param>
      <param key="0x01" name="Z-Wave Protocol Version" type="BYTE" typehashcode="0x01" comment="">
        <valueattrib key="0x00" hasdefines="false" showhex="true" />
      </param>
      <param key="0x02" name="Z-Wave Protocol Sub Version" type="BYTE" typehashcode="0x01" comment="">
        <valueattrib key="0x00" hasdefines="false" showhex="true" />
      </param>
      <param key="0x03" name="Firmware 0 Version" type="BYTE" typehashcode="0x01">
        <valueattrib key="0x00" hasdefines="false" showhex="true" />
      </param>
      <param key="0x04" name="Firmware 0 Sub Version" type="BYTE" typehashcode="0x01">
        <valueattrib key="0x00" hasdefines="false" showhex="true" />
      </param>
      <param key="0x05" name="Hardware Version" type="BYTE" typehashcode="0x01">
        <valueattrib key="0x00" hasdefines="false" showhex="true" />
      </param>
      <param key="0x06" name="Number of firmware targets" type="BYTE" typehashcode="0x01">
        <valueattrib key="0x00" hasdefines="false" showhex="true" />
      </param>
      <variant_group key="0x07" name="vg" variantKey="0x00" paramOffs="0x06" sizemask="0xFF" sizeoffs="0x00" typehashcode="0x0D" comment="">
        <param key="0x00" name="Firmware Version" type="BYTE" typehashcode="0x01" comment="">
          <valueattrib key="0x00" hasdefines="false" showhex="true" />
        </param>
        <param key="0x01" name="Firmware Sub Version" type="BYTE" typehashcode="0x01" comment="">
          <valueattrib key="0x00" hasdefines="false" showhex="true" />
        </param>
      </variant_group>
    </cmd>

there is a variant_group node. I believe this is what gets used for issues like you mentioned.

they have also made concessions for having multiple "values" in a single command. specific bytes representing values in OZW.

<cmd key="0x01" name="THERMOSTAT_FAN_MODE_SET" help="Thermostat Fan Mode Set" comment="">
      <param key="0x00" name="Level" type="STRUCT_BYTE" typehashcode="0x07" comment="">
        <fieldenum key="0x00" fieldname="Fan Mode" fieldmask="0x0F" shifter="0">
          <fieldenum value="Auto Low" />
          <fieldenum value="Low" />
          <fieldenum value="Auto High" />
          <fieldenum value="High" />
          <fieldenum value="Auto Medium" />
          <fieldenum value="Medium" />
        </fieldenum>
        <bitfield key="0x01" fieldname="Reserved" fieldmask="0x70" shifter="4" />
        <bitflag key="0x02" flagname="Off" flagmask="0x80" />
      </param>
    </cmd>

where "fieldenum key" would then become the index number in OZW.

t may be worth looking into. or not. the XML file seems to have most of the oddities already sorted out. I am sure there will be something that is not addressed. It is really close to the kind of a layout you have created on OZW.

By looking at the XML and looking at what you have for a design it's amazing how close you got to their original layout from reverse engineering. It's amazingly close.

petergebruers commented 5 years ago

@kdschlosser I like the way you dig into the available information, I'm trying to know and understand as much as possible of the spec, like you, so we seem to stumble on the same information from time to time. IMHO, that file, or variants of it, exist in several places as "ZWave_custom_cmd_classes.xml". I can find it in the Z/IP gateway (old version 2.8 and newest SDK 7), and in their "PC Controller software. I haven't had a close look at them because (a) they all have different sizes (b) they are not part of the published spec. Clearly, they are an interpretation of the spec. I would say those xml files are kind of 2nd source, but interesting indeed. I am trying to help @Fishwaldo with maintaining and updating this kind of stuff and when I do that, or post information, I try to refer to this: https://z-wavealliance.org/z-wave-public-specification/ - that's what Justin means when he said "We work off the CommandClass specification document(s)"

FWIW if you find the time and want to use that file in your project, I can say the version in the SDK 7 "Z/IP" sources is more recent, it says " at the top. I don't like to post this file here, I am unsure about the copyright, it is part of the sources of Z/IP so not very likely to be public. And I like to stay safe when it comes to copyright.

BTW the spec is public since 2016, not everything in OZW is based on "reverse engineering".

kdschlosser commented 5 years ago

I wasn't sure if the XML file might help in some manner. I personally find it easier to read code then API documentation. The problem is that API docs are usually written by the people that wrote the code. And what happens is that most people do not read API docs from the beginning to end for obvious reasons. And this is often the case is that the API is used as a reference. If there is not sufficient links in the docs to other portions of the docs it makes it very difficult to sometimes understand what is happening or going on. there are terms that get used that have no easy way of identifying what they are other then having to search. This is why I typically find it easier to read the code. and when done using an IDE a data path becomes available. Now an XML file is not exactly the ideal thing to read in terms of learning an API but it sure does shed some light.

I am also one for consistency in vocabulary used when porting an API to a different language. I will always do my best to keep the same object names used in the API. The XML file provides far more in that area then the documentation does.

BTW the spec is public since 2016, not everything in OZW is based on "reverse engineering".

I am aware. I have not gone back and looked at previous releases of OZW, But I would imagine that it was not rewritten in order to be in the same fashion as the API is. If there was something that was not right it was fixed. if there were things missing they were added. But the core layout I would imagine to be the same as it was before the API became public.

I did spend some time doing a better comparison between the value ID's and the XML file. I have to agree with @Fishwaldo, it is very close but not close enough. there would need to be to much code added in order to handle the shifts in the XML layout. It would not make it worth it. But at first glance it does look really close. it's simply not close enough.

Still interesting reading

petergebruers commented 5 years ago

I agree with almost everything you say and I have all the available source code on my laptop, like you. I also run Z/IP (from source code, very interesting!) because that is what Silabs says is mandatory for series 700 controllers, although behind the scenes it is still "plain old serial API"... I am very new to this so a lot of my time goes to understanding what the gateway does or assumes (eg HASS or Domoticz) then read and understand OZW code then match docs and like you've said have a peak at code, config files, ... This means, at the moment, the development is driven by the move from 1.4 to 1.6 and the resulting issues. Something comes up, @Fishwaldo and I chat (discord channel) about it and, at the moment, usually he fixes it (he can probably dream the source code, I am too slow).

it's simply not close enough

I think I understand what you mean, and I can give an example of that on the protocol level. After a time-out (in the OZW sense, in the main loop), OZW should abort ZW_SendData, it does not do that right now. It is on the agenda. One of the reasons is OZW has managed to work without it for a long time, its estimated impact is not high, though it might explain some odd phenomena.

So if you find one of those things, please do post them, I am listening...