Packages
grizzly
5.2.3
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/network_management_inclusion.ex
defmodule Grizzly.ZWave.CommandClasses.NetworkManagementInclusion do
@moduledoc """
Network Management Inclusion Command Class
This command class provides the commands for adding and removing Z-Wave nodes
to the Z-Wave network
"""
@behaviour Grizzly.ZWave.CommandClass
alias Grizzly.ZWave.{DSK, CommandClasses, Security}
@typedoc """
The status of the inclusion process
* `:done` - the inclusion process is done without error
* `:failed` - the inclusion process is done with failure, the device is not
included
* `:security_failed` - the inclusion process is done, the device is included
but their was an error during the security negotiations. Device \
functionality will be degraded.
"""
@type node_add_status() :: :done | :failed | :security_failed
@impl Grizzly.ZWave.CommandClass
def byte(), do: 0x34
@impl Grizzly.ZWave.CommandClass
def name(), do: :network_management_inclusion
@doc """
Parse the node add status byte into an atom
"""
@spec parse_node_add_status(0x06 | 0x07 | 0x09) :: node_add_status()
def parse_node_add_status(0x06), do: :done
def parse_node_add_status(0x07), do: :failed
def parse_node_add_status(0x09), do: :security_failed
@doc """
Encode a `node_add_status()` to a byte
"""
@spec node_add_status_to_byte(node_add_status()) :: 0x06 | 0x07 | 0x09
def node_add_status_to_byte(:done), do: 0x06
def node_add_status_to_byte(:failed), do: 0x07
def node_add_status_to_byte(:security_failed), do: 0x09
@typedoc """
Command classes have different ways they are support for each device
"""
@type tagged_command_classes() ::
{:non_secure_supported, [CommandClasses.command_class()]}
| {:non_secure_controlled, [CommandClasses.command_class()]}
| {:secure_supported, [CommandClasses.command_class()]}
| {:secure_controlled, [CommandClasses.command_class()]}
@typedoc """
Node info report
node information from a node add status report
* `:listening?` - is the device a listening device
* `:basic_device_class` - the basic device class
* `:generic_device_class` - the generic device class
* `:specific_device_class` - the specific device class
* `:command_classes` - list of command classes the new device supports
* `:keys_granted` - S2 keys granted by the user during the time of inclusion
version 2 and above
* `:kex_fail_type` - the type of key exchange failure if there is one version
2 and above
* `input_dsk` - the DSK of the device version 3 and above. If the info report is used
"""
@type node_info_report() :: %{
required(:seq_number) => byte(),
required(:node_id) => Grizzly.ZWave.node_id(),
required(:status) => node_add_status(),
required(:listening?) => boolean(),
required(:basic_device_class) => byte(),
required(:generic_device_class) => byte(),
required(:specific_device_class) => byte(),
required(:command_classes) => [tagged_command_classes()],
optional(:keys_granted) => [Security.key()],
optional(:kex_fail_type) => Security.key_exchange_fail_type(),
optional(:input_dsk) => Security.key_exchange_fail_type()
}
@typedoc """
Extended node info report
Node information from an extended node add status report
* `:listening?` - is the device a listening device
* `:basic_device_class` - the basic device class
* `:generic_device_class` - the generic device class
* `:specific_device_class` - the specific device class
* `:command_classes` - list of command classes the new device supports
* `:keys_granted` - S2 keys granted by the user during the time of inclusion
* `:kex_fail_type` - the type of key exchange failure if there is one
"""
@type extended_node_info_report() :: %{
required(:listening?) => boolean(),
required(:basic_device_class) => byte(),
required(:generic_device_class) => byte(),
required(:specific_device_class) => byte(),
required(:command_classes) => [tagged_command_classes()],
required(:keys_granted) => [Security.key()],
required(:kex_fail_type) => Security.key_exchange_fail_type()
}
@doc """
Parse node information from node add status and extended node add status reports
"""
@spec parse_node_info(binary()) :: node_info_report() | extended_node_info_report()
def parse_node_info(
<<node_info_length, listening?::1, _::7, _opt_func, basic_device_class,
generic_device_class, specific_device_class, more_info::binary>>
) do
# TODO: decode the command classes correctly (currently assuming no extended command classes)
# TODO: decode the device classes correctly
# node info length includes: node_info_length, listening?, opt_func, and 3 devices classes
# to get the length of command classes we have to subject 6 bytes.
command_class_length = node_info_length - 6
Map.new()
|> Map.put(:listening?, listening? == 1)
|> Map.put(:basic_device_class, basic_device_class)
|> Map.put(:generic_device_class, generic_device_class)
|> Map.put(:specific_device_class, specific_device_class)
|> parse_additional_node_info(more_info, command_class_length)
end
defp parse_additional_node_info(node_info, additional_info, command_class_length) do
<<command_classes_bin::binary-size(command_class_length), more_info::binary>> =
additional_info
command_classes = CommandClasses.command_class_list_from_binary(command_classes_bin)
node_info
|> Map.put(:command_classes, command_classes)
|> parse_optional_fields(more_info)
end
defp parse_optional_fields(info, <<>>), do: info
defp parse_optional_fields(info, <<keys_granted, kex_fail_type>>) do
info
|> put_security_info(keys_granted, kex_fail_type)
end
defp parse_optional_fields(info, <<keys_granted, kex_fail_type, 0x00>>) do
info
|> put_security_info(keys_granted, kex_fail_type)
end
defp parse_optional_fields(info, <<keys_granted, kex_fail_type, 16, dsk::binary-size(16)>>) do
info
|> put_security_info(keys_granted, kex_fail_type)
|> put_dsk(dsk)
end
defp put_security_info(info, keys_granted, kex_fail_type) do
info
|> Map.put(:keys_granted, Security.byte_to_keys(keys_granted))
|> Map.put(:kex_fail_type, Security.failed_type_from_byte(kex_fail_type))
end
defp put_dsk(info, dsk_bin) do
info
|> Map.put(:input_dsk, DSK.new(dsk_bin))
end
end