defmodule Grizzly.Network do
@moduledoc """
Module for working with the Z-Wave network
"""
alias Grizzly.{Associations, Connections, Report, SeqNumber, ZWave}
alias Grizzly.ZWave.Command
@typedoc """
Options for when you want to reset the device
- `:notify` - if the flag is set to true this will try to notify any node that
is part of the lifeline association group (default `true`)
"""
@type reset_opt() :: {:notify, boolean()}
@type opt() :: {:node_id, ZWave.node_id()}
@doc """
Get a list of node ids from the network
Just because a node id might be in the list does not mean the node is on the
network. A device might have been reset or unpaired from the controller with
out the controller knowing. However, in most use cases this shouldn't be an
issue.
Options
* `:node_id` - If your controller is part of another controller's network
you might want to issue network commands to that controller. By default
this option will chose your controller.
"""
@spec get_node_ids([opt()]) :: Grizzly.send_command_response()
def get_node_ids(opts \\ []) do
seq_number = SeqNumber.get_and_inc()
node_id = node_id_from_opts(opts)
Grizzly.send_command(node_id, :node_list_get, seq_number: seq_number)
end
@doc """
Reset the Z-Wave controller
This command takes a few seconds to run.
Options
* `:node_id` - If your controller is part of another controller's network
you might want to issue network commands to that controller. By default
this option will chose your controller.
"""
@spec reset_controller([reset_opt() | opt()]) :: Grizzly.send_command_response()
def reset_controller(opts \\ []) do
# close all the connections before resetting the controller. It's okay
# to blindly close all connections because when we send the command to
# the controller Grizzly will automatically reconnect to the controller
# at that point in time. We do this because the connections to the Z-Wave
# devices are still reachable after being removed and we will still be
# sending keep alive messages when we don't need to and will have
# unnecessary connections hanging out just taking up resources.
:ok = Connections.close_all()
seq_number = SeqNumber.get_and_inc()
node_id = node_id_from_opts(opts)
case Grizzly.send_command(node_id, :default_set, [seq_number: seq_number], timeout: 10_000) do
{:ok, %Report{type: :command, status: :complete}} = response ->
maybe_notify_reset(response, opts)
other ->
other
end
end
@doc """
Delete a node from the network's provisioning list via the node's DSK
Options
* `:node_id` - If your controller is part of another controller's network
you might want to issue network commands to that controller. By default
this option will chose your controller.
"""
@spec delete_node_provisioning(Grizzly.ZWave.DSK.t(), [opt()]) ::
Grizzly.send_command_response()
def delete_node_provisioning(dsk, opts \\ []) do
seq_number = SeqNumber.get_and_inc()
node_id = node_id_from_opts(opts)
Grizzly.send_command(node_id, :node_provisioning_delete, seq_number: seq_number, dsk: dsk)
end
@doc """
Get the nodes provisioning list information via the node's DSK
Options
* `:node_id` - If your controller is part of another controller's network
you might want to issue network commands to that controller. By default
this option will chose your controller.
"""
@spec get_node_provisioning(Grizzly.ZWave.DSK.t(), [opt()]) ::
Grizzly.send_command_response()
def get_node_provisioning(dsk, opts \\ []) do
seq_number = SeqNumber.get_and_inc()
node_id = node_id_from_opts(opts)
Grizzly.send_command(node_id, :node_provisioning_get, seq_number: seq_number, dsk: dsk)
end
@doc """
A node to the network provisioning list
Options
* `:node_id` - If your controller is part of another controller's network
you might want to issue network commands to that controller. By default
this option will chose your controller.
"""
@spec set_node_provisioning(
Grizzly.ZWave.DSK.t(),
[Grizzly.ZWave.SmartStart.MetaExtension.extension()],
[opt()]
) :: Grizzly.send_command_response()
def set_node_provisioning(dsk, meta_extensions, opts \\ []) do
seq_number = SeqNumber.get_and_inc()
node_id = node_id_from_opts(opts)
Grizzly.send_command(
node_id,
:node_provisioning_set,
seq_number: seq_number,
dsk: dsk,
meta_extensions: meta_extensions
)
end
@doc """
Add a long range device to the provisioning list
"""
@spec add_long_range_device(Grizzly.ZWave.DSK.t(), [opt()]) :: Grizzly.send_command_response()
def add_long_range_device(dsk, opts \\ []) do
extensions = [
bootstrapping_mode: :long_range,
smart_start_inclusion_setting: :pending,
advanced_joining: [:s2_unauthenticated, :s2_authenticated]
]
set_node_provisioning(dsk, extensions, opts)
end
@doc """
List all the nodes on the provisioning list
Options
* `:node_id` - If your controller is part of another controller's network
you might want to issue network commands to that controller. By default
this option will chose your controller.
"""
@spec list_node_provisionings(integer(), [opt()]) :: Grizzly.send_command_response()
def list_node_provisionings(remaining_counter, opts \\ []) do
seq_number = SeqNumber.get_and_inc()
node_id = node_id_from_opts(opts)
Grizzly.send_command(
node_id,
:node_provisioning_list_iteration_get,
seq_number: seq_number,
remaining_counter: remaining_counter
)
end
@doc """
Remove a (presumably) failed node
Options
* `:node_id` - If your controller is part of another controller's network
you might want to issue network commands to that controller. By default
this option will chose your controller.
"""
@spec remove_failed_node([opt()]) ::
Grizzly.send_command_response()
def remove_failed_node(opts \\ []) do
seq_number = SeqNumber.get_and_inc()
node_id = node_id_from_opts(opts)
Grizzly.send_command(:gateway, :failed_node_remove, seq_number: seq_number, node_id: node_id)
end
@doc """
Request a network update (network healing)
Options
* `:node_id` - If your controller is part of another controller's network
you might want to issue network commands to that controller. By default
this option will chose your controller.
"""
@spec request_network_update([opt()]) ::
Grizzly.send_command_response()
def request_network_update(opts \\ []) do
seq_number = SeqNumber.get_and_inc()
node_id = node_id_from_opts(opts)
Grizzly.send_command(node_id, :network_update_request, seq_number: seq_number)
end
defp node_id_from_opts(opts) do
Keyword.get(opts, :node_id, :gateway)
end
defp maybe_notify_reset(response, opts) do
{:ok, %Report{command: command}} = response
case Command.param!(command, :status) do
:done ->
maybe_notify_reset(opts)
response
:busy ->
response
end
end
defp maybe_notify_reset(opts) do
if Keyword.get(opts, :notify, true) do
notify_reset()
else
:ok
end
end
defp notify_reset() do
# get the nodes in the lifeline group
case Associations.get(1) do
nil ->
:ok
association ->
Enum.each(association.node_ids, fn node_id ->
Grizzly.send_command(node_id, :device_reset_locally_notification)
end)
end
end
end