defmodule Grizzly.ZWave.Commands.IndicatorSet do
@moduledoc """
This command is used to manipulate one or more indicator resources at a supporting node.
Params:
* `:value` - This field is used to enable or disable the indicator resource (required if v1 - optional and ignored otherwise)
* `:resources` - Indicator objects, each with an indicator id, property id and value (optional, v2+)
"""
@behaviour Grizzly.ZWave.Command
alias Grizzly.ZWave.{Command, DecodeError}
alias Grizzly.ZWave.CommandClasses.Indicator
@type param :: {:value, Indicator.value()} | {:resource, [Indicator.resource()]}
@impl true
@spec new([param()]) :: {:ok, Command.t()}
def new(params) do
command = %Command{
name: :indicator_set,
command_byte: 0x01,
command_class: Indicator,
params: params,
impl: __MODULE__
}
{:ok, command}
end
@impl true
@spec encode_params(Command.t()) :: binary()
def encode_params(command) do
value = Command.param(command, :value)
if value != nil do
<<value>>
else
resources = Command.param!(command, :resources)
resources_binary =
for resource <- resources, into: <<>> do
indicator_id_byte =
Keyword.fetch!(resource, :indicator_id) |> Indicator.indicator_id_to_byte()
property_id = Keyword.fetch!(resource, :property_id)
property_id_byte = Indicator.property_id_to_byte(property_id)
value = Keyword.fetch!(resource, :value) |> Indicator.value_to_byte(property_id)
<<indicator_id_byte, property_id_byte, value>>
end
count = Enum.count(resources)
<<0x00, 0x00::size(3), count::size(5)>> <> resources_binary
end
end
@impl true
@spec decode_params(binary()) :: {:ok, [param()]} | {:error, DecodeError.t()}
def decode_params(<<value>>) do
{:ok, [value: value]}
end
def decode_params(<<_ignored, 0x00::size(3), _count::size(5), resources_binary::binary>>) do
:binary.bin_to_list(resources_binary)
|> Enum.chunk_every(3)
|> Enum.reduce_while(
{:ok, [resources: []]},
fn [indicator_id_byte, property_id_byte, value], {:ok, [resources: acc]} ->
with {:ok, indicator_id} <- Indicator.indicator_id_from_byte(indicator_id_byte),
{:ok, property_id} <- Indicator.property_id_from_byte(property_id_byte),
{:ok, decoded_value} <- Indicator.value_from_byte(value, property_id) do
{:cont,
{:ok,
[
resources: [
[indicator_id: indicator_id, property_id: property_id, value: decoded_value] | acc
]
]}}
else
{:error, %DecodeError{} = decode_error} ->
{:halt, {:error, %DecodeError{decode_error | command: :indicator_set}}}
end
end
)
end
end