defmodule Grizzly.ZWave.CommandClasses.BarrierOperator do @moduledoc """ "BarrierOperator" Command Class The Barrier Operator Command Class is used to control and query the status of motorized barriers. """ @behaviour Grizzly.ZWave.CommandClass alias Grizzly.ZWave.DecodeError import Bitwise @type target_value :: :open | :close @type state :: :closed | 0x01..0x63 | :closing | :stopped | :opening | :open @type subsystem_type :: :audible_notification | :visual_notification @type subsystem_state :: :on | :off @impl Grizzly.ZWave.CommandClass def byte(), do: 0x66 @impl Grizzly.ZWave.CommandClass def name(), do: :barrier_operator @spec target_value_to_byte(:close | :open) :: 0x00 | 0xFF def target_value_to_byte(:close), do: 0x00 def target_value_to_byte(:open), do: 0xFF @spec target_value_from_byte(byte) :: {:error, Grizzly.ZWave.DecodeError.t()} | {:ok, :close | :open} def target_value_from_byte(0x00), do: {:ok, :close} def target_value_from_byte(0xFF), do: {:ok, :open} def target_value_from_byte(byte), do: {:error, %DecodeError{value: byte, param: :target_value}} @spec state_to_byte(state) :: byte def state_to_byte(:closed), do: 0x00 def state_to_byte(:closing), do: 0xFC def state_to_byte(:stopped), do: 0xFD def state_to_byte(:opening), do: 0xFE def state_to_byte(:open), do: 0xFF def state_to_byte(stopped_position) when stopped_position in 0x01..0x63, do: stopped_position @spec state_from_byte(byte) :: {:error, Grizzly.ZWave.DecodeError.t()} | {:ok, state} def state_from_byte(0x00), do: {:ok, :closed} def state_from_byte(0xFC), do: {:ok, :closing} def state_from_byte(0xFD), do: {:ok, :stopped} def state_from_byte(0xFE), do: {:ok, :opening} def state_from_byte(0xFF), do: {:ok, :open} def state_from_byte(byte) when byte in 0x01..0x63, do: {:ok, byte} def state_from_byte(byte), do: {:error, %DecodeError{value: byte, param: :state}} @doc "Converts subsystems into a bytes" @spec subsystem_types_to_bitmask([subsystem_type]) :: byte def subsystem_types_to_bitmask(subsystem_types) do subsystem_type_bytes = for subsystem_type <- subsystem_types, do: subsystem_type_to_byte(subsystem_type) integer = Enum.reduce(subsystem_type_bytes, 0x00, fn byte, acc -> acc ||| byte end) <> = <> byte end def bitmask_to_subsystem_types(byte) do bitmask = <> bits_on = for(<>, do: x) |> Enum.reverse() |> Enum.with_index(1) |> Enum.reduce([], fn {bit, index}, acc -> if bit == 1, do: [index | acc], else: acc end) Enum.reduce(bits_on, [], fn bit_on, acc -> case subsystem_type_from_byte(bit_on) do {:ok, subsystem_type} -> [subsystem_type | acc] _other -> acc end end) end @spec subsystem_type_to_byte(:audible_notification | :visual_notification) :: 0x01 | 0x02 def subsystem_type_to_byte(:audible_notification), do: 0x01 def subsystem_type_to_byte(:visual_notification), do: 0x02 @spec subsystem_type_from_byte(any) :: {:error, Grizzly.ZWave.DecodeError.t()} | {:ok, subsystem_type} def subsystem_type_from_byte(0x01), do: {:ok, :audible_notification} def subsystem_type_from_byte(0x02), do: {:ok, :visual_notification} def subsystem_type_from_byte(byte), do: {:error, %DecodeError{value: byte, param: :subsystem_type}} @spec subsystem_state_to_byte(:off | :on) :: 0x00 | 0xFF def subsystem_state_to_byte(:off), do: 0x00 def subsystem_state_to_byte(:on), do: 0xFF @spec subsystem_state_from_byte(byte) :: {:error, Grizzly.ZWave.DecodeError.t()} | {:ok, subsystem_state} def subsystem_state_from_byte(0x00), do: {:ok, :off} def subsystem_state_from_byte(0xFF), do: {:ok, :on} def subsystem_state_from_byte(byte), do: {:error, %DecodeError{value: byte, param: :subsystem_state}} end