Packages
grizzly
5.2.1
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/associations.ex
defmodule Grizzly.Associations do
@moduledoc false
use GenServer
alias Grizzly.{Options, ZWave}
@type grouping_id() :: byte()
defmodule State do
@moduledoc false
defstruct file_path: nil
end
defmodule Association do
@moduledoc false
alias Grizzly.ZWave
@type t() :: %__MODULE__{
grouping_id: byte(),
node_ids: [ZWave.node_id()]
}
defstruct grouping_id: nil, node_ids: []
end
@doc """
Start the Associations server
If you pass the GenServer option for a name you can access the server with
that name. However, if no name is passed to the `server_opts` then it will
fallback to using the `Grizzly.Associations` module name
"""
@spec start_link(Options.t(), GenServer.options()) :: GenServer.on_start()
def start_link(grizzly_options, server_opts \\ []) do
GenServer.start_link(
__MODULE__,
grizzly_options,
Keyword.put_new(server_opts, :name, __MODULE__)
)
end
@doc """
Save the node ids to the grouping id
"""
@spec save(GenServer.name(), grouping_id(), [ZWave.node_id()]) :: :ok
def save(server \\ __MODULE__, grouping_id, node_ids) do
GenServer.call(server, {:save, grouping_id, node_ids})
end
@doc """
Get all the associations
"""
@spec get_all(GenServer.name()) :: [Association.t()]
def get_all(server \\ __MODULE__) do
GenServer.call(server, :get_all)
end
@doc """
Get an association by the grouping id
"""
@spec get(GenServer.name(), grouping_id()) :: Association.t() | nil
def get(server \\ __MODULE__, grouping_id) do
GenServer.call(server, {:get, grouping_id})
end
@doc """
Delete all the associations
"""
@spec delete_all(GenServer.name()) :: :ok
def delete_all(server \\ __MODULE__) do
GenServer.call(server, :delete_all)
end
@doc """
Delete all the nodes from the grouping
"""
@spec delete_all_nodes_from_grouping(GenServer.name(), grouping_id()) :: :ok
def delete_all_nodes_from_grouping(server \\ __MODULE__, grouping_id) do
GenServer.call(server, {:delete_all_from_grouping, grouping_id})
end
@doc """
Delete the specified nodes from the association grouping
"""
@spec delete_nodes_from_grouping(GenServer.name(), grouping_id(), [ZWave.node_id()]) ::
:ok | {:error, :invalid_grouping_id}
def delete_nodes_from_grouping(server \\ __MODULE__, grouping_id, nodes) do
GenServer.call(server, {:remove_nodes_from_grouping, grouping_id, nodes})
end
@doc """
Delete the nodes from each association group
"""
@spec delete_nodes_from_all_groupings(GenServer.name(), [ZWave.node_id()]) :: :ok
def delete_nodes_from_all_groupings(server \\ __MODULE__, nodes) do
GenServer.call(server, {:delete_nodes_from_all_associations, nodes})
end
@impl GenServer
def init(%Options{associations_file: nil}) do
:ignore
end
def init(%Options{associations_file: file}) do
if File.exists?(file) do
{:ok, %State{file_path: file}}
else
try_create_file(file)
end
end
@impl GenServer
def handle_call({:save, grouping_id, node_ids}, _from, state) do
%State{file_path: file_path} = state
new_associations =
file_path
|> read_all_associations()
|> Map.put(grouping_id, node_ids)
write_associations(file_path, new_associations)
{:reply, :ok, state}
end
def handle_call(:get_all, _from, state) do
%State{file_path: file_path} = state
associations =
file_path
|> read_all_associations()
|> Enum.reduce([], fn {groupind_id, node_ids}, assocs ->
assoc = %Association{grouping_id: groupind_id, node_ids: node_ids}
assocs ++ [assoc]
end)
{:reply, associations, state}
end
def handle_call({:get, grouping_id}, _from, state) do
%State{file_path: file_path} = state
case Map.get(read_all_associations(file_path), grouping_id) do
nil ->
{:reply, nil, state}
node_ids ->
association = %Association{grouping_id: grouping_id, node_ids: node_ids}
{:reply, association, state}
end
end
def handle_call(:delete_all, _from, state) do
%State{file_path: path} = state
binary = :erlang.term_to_binary(%{})
:ok = File.write(path, binary)
{:reply, :ok, state}
end
def handle_call({:delete_all_from_grouping, grouping_id}, _from, state) do
%State{file_path: path} = state
{:ok, binary} = File.read(path)
updated_associations = Map.delete(:erlang.binary_to_term(binary), grouping_id)
:ok = File.write(path, :erlang.term_to_binary(updated_associations))
{:reply, :ok, state}
end
def handle_call({:remove_nodes_from_grouping, grouping_id, nodes}, _from, state) do
%State{file_path: path} = state
associations = read_all_associations(path)
new_associations =
try do
Map.update!(associations, grouping_id, fn old_node_ids ->
old_node_ids -- nodes
end)
rescue
KeyError ->
:invalid_grouping_id
end
case new_associations do
:invalid_grouping_id = error ->
{:reply, {:error, error}, state}
_ ->
:ok = File.write(path, :erlang.term_to_binary(new_associations))
{:reply, :ok, state}
end
end
def handle_call({:delete_nodes_from_all_associations, nodes}, _from, state) do
%State{file_path: path} = state
associations =
path
|> read_all_associations()
|> Enum.reduce(%{}, fn {grouping_id, ns}, new_associations ->
Map.put(new_associations, grouping_id, ns -- nodes)
end)
write_associations(path, associations)
{:reply, :ok, state}
end
defp try_create_file(path) do
try do
write_associations(path, %{})
{:ok, %State{file_path: path}}
rescue
_e ->
raise ArgumentError, """
Unable to create the file for associations
file_path: #{inspect(path)}
"""
end
end
defp write_associations(file_path, associations) do
binary = :erlang.term_to_binary(associations)
File.write!(file_path, binary)
end
defp read_all_associations(file_path) do
file_path
|> File.read!()
|> :erlang.binary_to_term()
end
end