Current section

Files

Jump to
grizzly lib grizzly zwave node_id.ex
Raw

lib/grizzly/zwave/node_id.ex

defmodule Grizzly.ZWave.NodeId do
@moduledoc false
# helper module for encoding and parsing node ids
alias Grizzly.ZWave
# When encoding for 16 bit node ids in the context of the node remove family of
# command (node ids > 255) the 8 bit node id byte of the binary needs to be set
# to 0xFF as per the specification.
# In ZWA_Z-Wave Network Protocol Command Class Specification 12.0.0.pdf:
# For example in sections 4.5.13.2 and 4.4.13.3:
# "This field MUST be set to 0xFF if the removed NodeID is greater than 255."
# This only is used for version 4 parsing and encoding.
@node_id_is_16_bit 0xFF
@doc """
Encode the node id
"""
@spec encode(ZWave.node_id()) :: binary()
def encode(node_id), do: <<node_id>>
@typedoc """
Options for extended format encoding
* `:delimiter` - when the 8 bit node id and 16 bit node id bytes do not follow
in sequence next to each other, this option allows the information between
the two formats to be passed.
"""
@type extended_encoding_opt() :: {:delimiter, binary()}
@doc """
Encode the node id using the extended node id format
The format for command classes that support extended node ids is:
```
<<0xFF, 16_bit_node_id>>
```
Or
```
<<8_bit_node_id, 16_bits_that_match_8_bit_node_id>>
```
When using the extended format encoding as passing an 8 bit node id the
specification often times states that the remaining 2 bytes at the end of the
binary must match the node id passed to the 8 bit node id byte. For example:
```elixir
iex> encode_extended(0x05)
<<0x05, 0x00, 0x05>>
```
Since the node id fits within the 8 bit node id field that is the first byte.
The next two bytes (16 bits) is that node id repeated within the 2 byte space
required to fill the total number of bytes for the extended format encoding.
When the 8 bit node id and the 16 bit node id bytes are not in sequence and
there other information between them you can pass the `:delimiter` option to
ensure that binary is in between the 8 bit node and the 16 bit node id bytes.
```elixir
iex> encode_extended(0x10, delimiter: <<0xA0, 0xB0>>)
<<0x10, 0xA0, 0xB0, 0x00, 0x10>>
iex> encode_extended(0x1010, delimiter: <<0xA0, 0xB0>>)
<<0xFF, 0xA0, 0xB0, 0x10, 0x10>>
```
"""
@spec encode_extended(ZWave.node_id(), [extended_encoding_opt()]) :: binary()
def encode_extended(node_ids, opts \\ [])
def encode_extended(node_id, opts) when node_id < 233 do
case Keyword.get(opts, :delimiter) do
nil ->
<<node_id, node_id::16>>
padding ->
<<node_id, padding::binary, node_id::16>>
end
end
def encode_extended(node_id, opts) when node_id > 255 and node_id <= 65535 do
case Keyword.get(opts, :delimiter) do
nil ->
<<@node_id_is_16_bit, node_id::16>>
padding ->
<<@node_id_is_16_bit, padding::binary, node_id::16>>
end
end
@typedoc """
Parsing options
* `:delimiter_size` - when the 8 bit node id and the 16 bit node are not in
sequence and are separated by bytes in between them you can specify the byte
size of the delimiter.
"""
@type parse_opt() :: {:delimiter_size, non_neg_integer()}
@doc """
Parse the binary that contains the node id
For node id binaries that support the extended node id format but contain
bytes in between the 8 bit node id byte and the 16 bit node id bytes the
options `:delimiter_size` can be passed to account for these in between
bytes in parsing.
"""
@spec parse(binary(), [parse_opt()]) :: ZWave.node_id()
def parse(node_id_binary, opts \\ [])
def parse(<<node_id>>, _opts), do: node_id
def parse(node_id_bin, opts) do
case Keyword.get(opts, :delimiter_size, 0) do
0 ->
do_parse(node_id_bin)
del_size ->
<<node_id_8, _delimiter::size(del_size)-unit(8), node_id_16::binary>> = node_id_bin
do_parse(<<node_id_8, node_id_16::binary>>)
end
end
defp do_parse(<<node_id>>), do: node_id
# sometimes packets will have extra bytes after the 16 bit node id, we can
# ignore those
defp do_parse(<<@node_id_is_16_bit, node_id::16, _ignore::binary>>), do: node_id
defp do_parse(<<node_id, _ignored::binary>>), do: node_id
end