Packages
grizzly
6.8.8
9.1.4
9.1.2
9.1.1
9.1.0
9.0.0
8.15.3
8.15.2
8.15.1
8.15.0
8.14.0
8.13.0
8.12.0
8.11.3
8.11.2
8.11.1
8.11.0
8.10.0
8.9.0
8.8.1
8.8.0
8.7.1
8.7.0
8.6.12
8.6.11
8.6.10
8.6.9
8.6.8
8.6.7
retired
8.6.6
8.6.5
8.6.4
8.6.3
8.6.2
8.6.1
8.6.0
8.5.3
8.5.2
8.5.1
8.5.0
8.4.0
8.3.0
8.2.3
8.2.2
8.2.1
8.2.0
8.1.0
8.0.1
8.0.0
7.4.3
7.4.2
7.4.1
7.4.0
7.3.0
7.2.0
7.1.4
7.1.3
7.1.2
7.1.1
7.1.0
7.0.4
7.0.3
7.0.2
7.0.1
7.0.0
6.8.8
6.8.7
6.8.6
6.8.5
6.8.4
6.8.3
6.8.2
6.8.1
6.8.0
6.7.1
6.7.0
6.6.1
6.6.0
6.5.1
6.5.0
6.4.0
6.3.0
6.2.0
6.1.1
6.1.0
6.0.1
6.0.0
5.4.1
5.4.0
5.3.0
5.2.8
5.2.7
5.2.6
5.2.5
5.2.4
5.2.3
5.2.2
5.2.1
5.2.0
5.1.2
5.1.1
5.1.0
5.0.2
5.0.1
5.0.0
4.0.1
4.0.0
3.0.0
2.1.0
2.0.0
1.0.1
1.0.0
0.22.7
0.22.6
0.22.5
0.22.4
0.22.3
0.22.2
0.22.1
0.22.0
0.21.1
0.21.0
0.20.2
0.20.1
0.20.0
0.19.1
0.19.0
0.18.3
0.18.2
0.18.1
0.18.0
0.17.7
0.17.6
0.17.5
0.17.4
0.17.3
0.17.2
0.17.1
0.17.0
0.16.2
0.16.1
0.16.0
0.15.11
0.15.10
0.15.9
0.15.8
0.15.7
0.15.6
0.15.5
0.15.4
0.15.3
0.15.2
0.15.1
0.15.0
0.14.8
0.14.7
0.14.6
0.14.5
0.14.4
0.14.3
0.14.2
0.14.1
0.14.0
0.13.0
0.12.3
0.12.2
0.12.1
0.12.0
0.11.0
0.10.3
0.10.2
0.10.1
0.10.0
0.9.0
0.9.0-rc.4
0.9.0-rc.3
0.9.0-rc.2
0.9.0-rc.1
0.9.0-rc.0
0.8.8
0.8.7
0.8.6
0.8.5
0.8.4
0.8.3
0.8.2
0.8.1
0.8.0
0.7.0
0.6.6
0.6.5
0.6.4
0.6.3
0.6.2
0.6.1
0.6.0
0.5.0
0.4.3
0.4.2
Elixir Z-Wave library
Current section
Files
Jump to
Current section
Files
lib/grizzly/zwave/command_classes.ex
defmodule Grizzly.ZWave.CommandClasses do
require Logger
@type command_class_list :: [
non_secure_supported: list(atom()),
non_secure_controlled: list(atom()),
secure_supported: list(atom()),
secure_controlled: list(atom())
]
defmodule Generate do
@moduledoc false
import Grizzly.ZWave.GeneratedMappings, only: [command_class_mappings: 0]
defmacro __before_compile__(_) do
mappings = command_class_mappings()
to_byte =
for {byte, command_class} <- mappings do
quote do
def to_byte(unquote(command_class)), do: unquote(byte)
end
end
from_byte =
for {byte, command_class} <- mappings do
quote do
def from_byte(unquote(byte)), do: {:ok, unquote(command_class)}
end
end
command_classes =
mappings
|> Enum.map(&elem(&1, 1))
|> Enum.reverse()
|> Enum.reduce(&{:|, [], [&1, &2]})
quote do
@type command_class :: unquote(command_classes)
@doc """
Try to parse the byte into a command class
"""
@spec from_byte(byte()) :: {:ok, command_class()} | {:error, :unsupported_command_class}
unquote(from_byte)
def from_byte(byte) do
Logger.warning(
"[Grizzly] Unsupported command class from byte #{inspect(byte, base: :hex)}"
)
{:error, :unsupported_command_class}
end
@doc """
Get the byte representation of the command class
"""
@spec to_byte(command_class()) :: byte()
unquote(to_byte)
end
end
end
@before_compile Generate
@doc """
Turn the list of command classes into the binary representation outlined in
the Network-Protocol command class specification.
TODO: add more details
"""
@spec command_class_list_to_binary([command_class_list()]) :: binary()
def command_class_list_to_binary(command_class_list) do
non_secure_supported = Keyword.get(command_class_list, :non_secure_supported, [])
non_secure_controlled = Keyword.get(command_class_list, :non_secure_controlled, [])
secure_supported = Keyword.get(command_class_list, :secure_supported, [])
secure_controlled = Keyword.get(command_class_list, :secure_controlled, [])
non_secure_supported_bin = for cc <- non_secure_supported, into: <<>>, do: <<to_byte(cc)>>
non_secure_controlled_bin = for cc <- non_secure_controlled, into: <<>>, do: <<to_byte(cc)>>
secure_supported_bin = for cc <- secure_supported, into: <<>>, do: <<to_byte(cc)>>
secure_controlled_bin = for cc <- secure_controlled, into: <<>>, do: <<to_byte(cc)>>
bin =
non_secure_supported_bin
|> maybe_concat_command_classes(:non_secure_controlled, non_secure_controlled_bin)
|> maybe_concat_command_classes(:secure_supported, secure_supported_bin)
|> maybe_concat_command_classes(:secure_controlled, secure_controlled_bin)
if bin == <<>> do
<<0>>
else
bin
end
end
@doc """
Turn the binary representation that is outlined in the Network-Protocol specs
"""
@spec command_class_list_from_binary(binary()) :: [command_class_list()]
def command_class_list_from_binary(binary) do
binary_list = :erlang.binary_to_list(binary)
{_, command_classes} =
Enum.reduce(
binary_list,
{:non_secure_supported,
[
non_secure_supported: [],
non_secure_controlled: [],
secure_supported: [],
secure_controlled: []
]},
fn
0xEF, {:non_secure_supported, command_classes} ->
{:non_secure_controlled, command_classes}
0xF1, {_, command_classes} ->
{:secure_supported, command_classes}
0x00, {_, command_classes} ->
{:secure_supported, command_classes}
0xEF, {:secure_supported, command_classes} ->
{:secure_controlled, command_classes}
command_class_byte, {security, command_classes}
when command_class_byte not in [0xF1, 0xEF, 0x00] ->
case from_byte(command_class_byte) do
{:ok, command_class} ->
{security,
Keyword.update(command_classes, security, [], &(&1 ++ [command_class]))}
{:error, :unsupported_command_class} ->
# Skip unsupported command classes
{security, command_classes}
end
end
)
command_classes
end
def maybe_concat_command_classes(binary, _, <<>>), do: binary
def maybe_concat_command_classes(binary, :non_secure_controlled, ccs_bin),
do: binary <> <<0xEF, ccs_bin::binary>>
def maybe_concat_command_classes(binary, :secure_supported, ccs_bin),
do: binary <> <<0xF1, 0x00, ccs_bin::binary>>
def maybe_concat_command_classes(binary, :secure_controlled, ccs_bin),
do: binary <> <<0xEF, ccs_bin::binary>>
end