diff --git a/lib/grizzly/commands/table.ex b/lib/grizzly/commands/table.ex index a59fb559..92f6ef57 100644 --- a/lib/grizzly/commands/table.ex +++ b/lib/grizzly/commands/table.ex @@ -267,6 +267,9 @@ defmodule Grizzly.Commands.Table do {:thermostat_setpoint_supported_get, {Commands.ThermostatSetpointSupportedGet, handler: {WaitReport, complete_report: :thermostat_setpoint_supported_report}}}, + {:thermostat_setpoint_capabilities_get, + {Commands.ThermostatSetpointCapabilitiesGet, + handler: {WaitReport, complete_report: :thermostat_setpoint_capabilities_report}}}, # Thermostat fan mode {:thermostat_fan_mode_set, {Commands.ThermostatFanModeSet, handler: AckResponse}}, diff --git a/lib/grizzly/zwave/commands/thermostat_setpoint_capabilities_get.ex b/lib/grizzly/zwave/commands/thermostat_setpoint_capabilities_get.ex new file mode 100644 index 00000000..df36ece2 --- /dev/null +++ b/lib/grizzly/zwave/commands/thermostat_setpoint_capabilities_get.ex @@ -0,0 +1,43 @@ +defmodule Grizzly.ZWave.Commands.ThermostatSetpointCapabilitiesGet do + @moduledoc """ + This command is used request the supported setpoint value range for a setpoint type. + + ## Parameters + + * `:type` - The setpoint type to query capabilities for. + """ + + @behaviour Grizzly.ZWave.Command + + alias Grizzly.ZWave.{Command, DecodeError} + alias Grizzly.ZWave.CommandClasses.ThermostatSetpoint + + @type param() :: {:type, ThermostatSetpoint.type()} + + @impl Grizzly.ZWave.Command + @spec new([param()]) :: {:ok, Command.t()} + def new(params) do + command = %Command{ + name: :thermostat_setpoint_capabilities_get, + command_byte: 0x09, + command_class: ThermostatSetpoint, + params: params, + impl: __MODULE__ + } + + {:ok, command} + end + + @impl Grizzly.ZWave.Command + @spec encode_params(Command.t()) :: binary() + def encode_params(command) do + type = Command.param!(command, :type) + <<0::4, ThermostatSetpoint.encode_type(type)::4>> + end + + @impl Grizzly.ZWave.Command + @spec decode_params(binary()) :: {:ok, [param()]} | {:error, DecodeError.t()} + def decode_params(<<_reserved::4, type::4>>) do + {:ok, [type: ThermostatSetpoint.decode_type(type)]} + end +end diff --git a/lib/grizzly/zwave/commands/thermostat_setpoint_capabilities_report.ex b/lib/grizzly/zwave/commands/thermostat_setpoint_capabilities_report.ex new file mode 100644 index 00000000..b40f1285 --- /dev/null +++ b/lib/grizzly/zwave/commands/thermostat_setpoint_capabilities_report.ex @@ -0,0 +1,82 @@ +defmodule Grizzly.ZWave.Commands.ThermostatSetpointCapabilitiesReport do + @moduledoc """ + This command is used advertise the supported setpoint value range for a given setpoint type. + + ## Parameters + + * `:type` - the setpoint type + * `:min_scale` - scale of the minimum value + * `:min_value` - minimum value + * `:max_scale` - scale of the maximum value + * `:max_value` - maximum value + """ + + @behaviour Grizzly.ZWave.Command + + import Grizzly.ZWave.CommandClasses.ThermostatSetpoint + import Grizzly.ZWave.Encoding + + alias Grizzly.ZWave.{Command, DecodeError} + alias Grizzly.ZWave.CommandClasses.ThermostatSetpoint + + @type param :: + {:type, ThermostatSetpoint.type()} + | {:min_scale, ThermostatSetpoint.scale()} + | {:min_value, number()} + | {:max_scale, ThermostatSetpoint.scale()} + | {:max_value, number()} + + @impl Grizzly.ZWave.Command + @spec new([param()]) :: {:ok, Command.t()} + def new(params) do + command = %Command{ + name: :thermostat_setpoint_capabilities_report, + command_byte: 0x0A, + command_class: ThermostatSetpoint, + params: params, + impl: __MODULE__ + } + + {:ok, command} + end + + @impl Grizzly.ZWave.Command + @spec encode_params(Command.t()) :: binary() + def encode_params(command) do + type = Command.param!(command, :type) + min_scale = Command.param!(command, :min_scale) + min_value = Command.param!(command, :min_value) + max_scale = Command.param!(command, :max_scale) + max_value = Command.param!(command, :max_value) + + {min_int_value, min_precision, min_byte_size} = encode_zwave_float(min_value) + {max_int_value, max_precision, max_byte_size} = encode_zwave_float(max_value) + + <<0::4, encode_type(type)::4, min_precision::3, encode_scale(min_scale)::2, min_byte_size::3, + min_int_value::size(min_byte_size)-unit(8), max_precision::3, encode_scale(max_scale)::2, + max_byte_size::3, max_int_value::size(max_byte_size)-unit(8)>> + end + + @impl Grizzly.ZWave.Command + @spec decode_params(binary()) :: {:ok, [param()]} | {:error, DecodeError.t()} + def decode_params( + <<_::4, type::4, min_precision::3, min_scale::2, min_byte_size::3, + min_int_value::size(min_byte_size)-unit(8), max_precision::3, max_scale::2, + max_byte_size::3, max_int_value::size(max_byte_size)-unit(8)>> + ) do + with {:ok, min_scale} <- decode_scale(min_scale), + {:ok, max_scale} <- decode_scale(max_scale) do + {:ok, + [ + type: decode_type(type), + min_scale: min_scale, + min_value: decode_zwave_float(min_int_value, min_precision), + max_scale: max_scale, + max_value: decode_zwave_float(max_int_value, max_precision) + ]} + else + {:error, %DecodeError{} = err} -> + {:error, %{err | command: :thermostat_setpoint_capabilities_report}} + end + end +end diff --git a/lib/grizzly/zwave/decoder.ex b/lib/grizzly/zwave/decoder.ex index 6d22fd04..f06c6306 100644 --- a/lib/grizzly/zwave/decoder.ex +++ b/lib/grizzly/zwave/decoder.ex @@ -324,6 +324,8 @@ defmodule Grizzly.ZWave.Decoder do {0x43, 0x03, Commands.ThermostatSetpointReport}, {0x43, 0x04, Commands.ThermostatSetpointSupportedGet}, {0x43, 0x05, Commands.ThermostatSetpointSupportedReport}, + {0x43, 0x09, Commands.ThermostatSetpointCapabilitiesGet}, + {0x43, 0x0A, Commands.ThermostatSetpointCapabilitiesReport}, # Thermostat fan mode {0x44, 0x01, Commands.ThermostatFanModeSet}, diff --git a/test/grizzly/zwave/commands/thermostat_setpoint_capabilities_get_test.exs b/test/grizzly/zwave/commands/thermostat_setpoint_capabilities_get_test.exs new file mode 100644 index 00000000..e769d5a3 --- /dev/null +++ b/test/grizzly/zwave/commands/thermostat_setpoint_capabilities_get_test.exs @@ -0,0 +1,22 @@ +defmodule Grizzly.ZWave.Commands.ThermostatSetpointCapabilitiesGetTest do + use ExUnit.Case, async: true + + alias Grizzly.ZWave.Commands.ThermostatSetpointCapabilitiesGet + + test "encodes params correctly" do + params = [ + type: :heating + ] + + {:ok, cmd} = ThermostatSetpointCapabilitiesGet.new(params) + + expected_binary = <<0::4, 1::4>> + assert expected_binary == ThermostatSetpointCapabilitiesGet.encode_params(cmd) + end + + test "decodes params correctly" do + binary = <<0::4, 1::4>> + + {:ok, [type: :heating]} = ThermostatSetpointCapabilitiesGet.decode_params(binary) + end +end diff --git a/test/grizzly/zwave/commands/thermostat_setpoint_capabilities_report_test.exs b/test/grizzly/zwave/commands/thermostat_setpoint_capabilities_report_test.exs new file mode 100644 index 00000000..02b8380f --- /dev/null +++ b/test/grizzly/zwave/commands/thermostat_setpoint_capabilities_report_test.exs @@ -0,0 +1,27 @@ +defmodule Grizzly.ZWave.Commands.ThermostatSetpointCapabilitiesReportTest do + use ExUnit.Case, async: true + + alias Grizzly.ZWave.Commands.ThermostatSetpointCapabilitiesReport + + test "encodes params correctly" do + params = [ + type: :heating, + min_scale: :c, + min_value: 10.5, + max_scale: :c, + max_value: 30 + ] + + {:ok, cmd} = ThermostatSetpointCapabilitiesReport.new(params) + + expected_binary = <<0::4, 1::4, 1::3, 0::2, 1::3, 105, 0::3, 0::2, 1::3, 30>> + assert expected_binary == ThermostatSetpointCapabilitiesReport.encode_params(cmd) + end + + test "decodes params correctly" do + binary = <<0::4, 1::4, 1::3, 0::2, 1::3, 105, 0::3, 0::2, 1::3, 30>> + + {:ok, [type: :heating, min_scale: :c, min_value: 10.5, max_scale: :c, max_value: 30]} = + ThermostatSetpointCapabilitiesReport.decode_params(binary) + end +end