lib/grizzly/zwave/device_class.ex

defmodule Grizzly.ZWave.DeviceClass do
  @moduledoc """
  Z-Wave device classes

  Z-Wave device classes allow grouping devices with the same functionality
  together. Moreover, the device class specification for specific device classes
  provide mandatory and recommended command class support.
  """

  alias Grizzly.ZWave.{CommandClasses, DeviceClasses}
  alias Grizzly.ZWave.Commands.VersionReport

  @type command_class_version() :: integer()

  @type command_class_spec() :: %{
          support: %{required(CommandClasses.command_class()) => command_class_version()},
          control: %{required(CommandClasses.command_class()) => command_class_version()}
        }

  @type t() :: %{
          basic_device_class: DeviceClasses.basic_device_class(),
          generic_device_class: DeviceClasses.generic_device_class(),
          specific_device_class: DeviceClasses.specific_device_class(),
          command_classes: command_class_spec(),
          manufacturer_id: non_neg_integer(),
          product_id: non_neg_integer(),
          product_type_id: non_neg_integer(),
          library_type: VersionReport.library_type()
        }

  @doc """
  Get the command class version for the command class
  """
  @spec get_command_class_version(t(), CommandClasses.command_class()) :: non_neg_integer() | nil
  def get_command_class_version(device_class, command_class) do
    device_class.command_classes.control
    |> Map.merge(device_class.command_classes.support)
    |> Enum.find_value(fn
      {^command_class, version} -> version
      _ -> nil
    end)
  end

  @doc """
  Check if the device class specification includes a particular command class
  """
  @spec has_command_class?(t(), CommandClasses.command_class()) :: boolean()
  def has_command_class?(device_class, command_class) do
    device_class.command_classes.control
    |> Map.merge(device_class.command_classes.support)
    |> Enum.find_value(fn
      {^command_class, _version} -> true
      _ -> false
    end)
  end

  @doc """
  A device class for a HVAC thermostat
  """
  @spec thermostat_hvac() :: t()
  def thermostat_hvac() do
    %{
      basic_device_class: :routing_end_node,
      generic_device_class: :thermostat,
      specific_device_class: :thermostat_general_v2,
      command_classes: %{
        support: %{
          alarm: 3,
          association: 2,
          association_group_information: 1,
          basic: 1,
          battery: 1,
          device_reset_locally: 1,
          manufacturer_specific: 2,
          power_level: 1,
          security_2: 1,
          supervision: 1,
          thermostat_mode: 3,
          thermostat_fan_mode: 1,
          thermostat_fan_state: 1,
          thermostat_setpoint: 2,
          transport_service: 2,
          sensor_multilevel: 5,
          version: 2,
          zwave_plus_info: 2
        },
        control: %{}
      },
      manufacturer_id: 0x0000,
      product_id: 0x0000,
      product_type_id: 0x0000,
      library_type: :routing_end_node
    }
  end

  @doc """
  Device class spec for a generic multilevel sensor
  """
  @spec multilevel_sensor() :: t()
  def multilevel_sensor() do
    %{
      basic_device_class: :routing_end_node,
      generic_device_class: :sensor_multilevel,
      specific_device_class: :routing_sensor_multilevel,
      command_classes: %{
        support: %{
          alarm: 3,
          association: 2,
          association_group_information: 1,
          basic: 1,
          battery: 1,
          device_reset_locally: 1,
          manufacturer_specific: 2,
          security_2: 1,
          sensor_multilevel: 5,
          version: 2,
          zwave_plus_info: 2
        },
        control: %{}
      },
      manufacturer_id: 0x0000,
      product_id: 0x0000,
      product_type_id: 0x0000,
      library_type: :routing_end_node
    }
  end
end