Packages
grizzly
9.1.4
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/storage.ex
defmodule Grizzly.Storage do
@moduledoc """
Persistent storage for device information and other Grizzly internals.
"""
alias Grizzly.ZWave
alias Grizzly.ZWave.Command
alias Grizzly.ZWave.CommandClasses
alias Grizzly.ZWave.CommandClasses.NetworkManagementInclusion, as: NMI
alias Grizzly.ZWave.DeviceClasses
alias Grizzly.ZWave.DSK
alias Grizzly.ZWave.Security
@typedoc """
See `t:PropertyTable.property/0`.
"""
@type key :: PropertyTable.property()
@typedoc """
See `t:PropertyTable.value/0`.
"""
@type value :: PropertyTable.value()
@typedoc """
See `t:PropertyTable.pattern/0`.
"""
@type pattern :: PropertyTable.pattern()
@type put_node_info_opt() :: {:overwrite_command_classes, boolean()}
@type node_info() :: %{
listening?: boolean(),
basic_device_class: DeviceClasses.basic_device_class(),
generic_device_class: DeviceClasses.generic_device_class(),
specific_device_class: DeviceClasses.specific_device_class(),
command_classes: CommandClasses.command_class_list()
}
@type node_inclusion_info() :: %{
status: NMI.node_add_status(),
granted_keys: [Security.key()],
kex_fail_type: Security.key_exchange_fail_type() | nil,
smartstart?: boolean()
}
@doc """
Returns a child spec to start the storage process under a supervisor.
See `Supervisor`.
"""
@spec child_spec(PropertyTable.options()) :: Supervisor.child_spec()
def child_spec(opts) do
opts
|> Keyword.merge(name: __MODULE__)
|> PropertyTable.child_spec()
end
@doc """
Puts a key-value pair into storage using the configured adapter.
"""
@spec put(key(), value()) :: :ok
def put(key, value) do
{adapter, arg} = adapter()
adapter.put(arg, key, value)
end
@doc """
Puts multiple key-value pairs into storage using the configured adapter.
"""
@spec put_many([{key(), value()}]) :: :ok
def put_many(properties) do
{adapter, arg} = adapter()
adapter.put_many(arg, properties)
end
@doc """
Get a value from storage by key using the configured adapter.
"""
@spec get(key()) :: value()
def get(key) do
{adapter, arg} = adapter()
adapter.get(arg, key)
end
@doc """
Match keys in storage against a pattern using the configured adapter.
"""
@spec match(pattern()) :: [{key(), value()}]
def match(pattern) do
{adapter, arg} = adapter()
adapter.match(arg, pattern)
end
@doc """
Delete keys matching a pattern using the configured adapter.
"""
@spec delete_matches(pattern()) :: :ok
def delete_matches(pattern) do
{adapter, arg} = adapter()
adapter.delete_matches(arg, pattern)
end
@doc """
Delete everything from storage.
"""
@spec delete_all() :: :ok
def delete_all(), do: delete_matches([])
@doc """
Stores information about a node's inclusion process.
"""
@spec put_node_inclusion_info(ZWave.node_id(), node_inclusion_info()) :: :ok
def put_node_inclusion_info(
node_id,
%{status: _, granted_keys: _, kex_fail_type: _, smartstart?: _} = info
) do
put(node_key(node_id, "inclusion_info"), info)
end
@doc """
Retrieves information about a node's inclusion process.
"""
@spec get_node_inclusion_info(ZWave.node_id()) :: node_inclusion_info() | nil
def get_node_inclusion_info(node_id) do
get(node_key(node_id, "inclusion_info"))
end
@doc """
Stores node information.
By default, command classes are merged using `CommandClasses.merge/2` to prevent
loss of data due to Z/IP Gateway quirks. To force the use of the new command class
list, use the `:overwrite_command_classes` option.
"""
@spec put_node_info(ZWave.node_id(), Command.t() | node_info(), [put_node_info_opt()]) :: :ok
def put_node_info(node_id, info, opts \\ [])
def put_node_info(
node_id,
%{
listening?: _,
basic_device_class: _,
generic_device_class: _,
specific_device_class: _,
command_classes: _
} = info,
opts
) do
info =
if Keyword.get(opts, :overwrite_command_classes) == true do
info
else
Map.update(info, :command_classes, info.command_classes, fn current_cc_list ->
CommandClasses.merge(current_cc_list, info.command_classes)
end)
end
put(node_key(node_id, "info"), info)
end
def put_node_info(node_id, %Command{name: :node_info_cached_report} = cmd, opts) do
info = %{
listening?: Command.param!(cmd, :listening?),
basic_device_class: Command.param!(cmd, :basic_device_class),
generic_device_class: Command.param!(cmd, :generic_device_class),
specific_device_class: Command.param!(cmd, :specific_device_class),
command_classes: Command.param!(cmd, :command_classes)
}
put_node_info(node_id, info, opts)
end
@doc """
Retrieves a node's basic info.
"""
@spec get_node_info(ZWave.node_id()) :: node_info() | nil
def get_node_info(node_id) do
get(node_key(node_id, "info"))
end
@doc """
Store a node's DSK.
"""
@spec put_node_dsk(ZWave.node_id(), DSK.t()) :: :ok
def put_node_dsk(node_id, %DSK{} = dsk) do
put(node_key(node_id, "dsk"), DSK.to_string(dsk))
end
@doc """
Returns a node's stored DSK.
"""
@spec get_node_dsk(ZWave.node_id()) :: DSK.t() | nil
def get_node_dsk(node_id) do
case get(node_key(node_id, "dsk")) do
nil -> nil
dsk_string -> DSK.parse!(dsk_string)
end
end
@doc """
Stores a node's wakeup interval in seconds.
"""
@spec put_node_wakeup_interval(ZWave.node_id(), non_neg_integer()) :: :ok
def put_node_wakeup_interval(node_id, interval) do
put(node_key(node_id, ["wakeup_interval"]), interval)
end
@doc """
Returns the node's wakeup interval in seconds.
"""
@spec get_node_wakeup_interval(ZWave.node_id()) :: non_neg_integer() | nil
def get_node_wakeup_interval(node_id) do
get(node_key(node_id, ["wakeup_interval"]))
end
@doc """
Stores a node's last awake time.
"""
@spec put_node_last_awake(ZWave.node_id(), DateTime.t()) :: :ok
def put_node_last_awake(node_id, last_awake) do
put(node_key(node_id, ["last_awake"]), last_awake)
end
@doc """
Retrieves a node's last awake time.
"""
@spec get_node_last_awake(ZWave.node_id()) :: DateTime.t() | nil
def get_node_last_awake(node_id) do
get(node_key(node_id, ["last_awake"]))
end
@doc """
Stores a node's command class version.
"""
@spec put_node_command_class_version(ZWave.node_id(), atom(), pos_integer()) :: :ok
def put_node_command_class_version(node_id, command_class, version) do
put(node_key(node_id, ["command_classes", "#{command_class}", "version"]), version)
end
@doc """
Retrieves a node's command class version.
"""
@spec get_node_command_class_version(ZWave.node_id(), atom()) :: pos_integer() | nil
def get_node_command_class_version(node_id, command_class) do
get(node_key(node_id, ["command_classes", "#{command_class}", "version"]))
end
defp node_key(:gateway, rest), do: ["node", "1" | List.wrap(rest)]
defp node_key(node_id, rest), do: ["node", "#{node_id}" | List.wrap(rest)]
defp adapter() do
{_adapter, _arg} = Grizzly.options().storage_adapter
end
end