Packages
grizzly
5.0.2
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/virtual_devices/thermostat.ex
defmodule Grizzly.VirtualDevices.Thermostat do
@moduledoc """
Implementation of a virtual device for a thermostat
"""
use GenServer
@behaviour Grizzly.VirtualDevices.Device
alias Grizzly.VirtualDevices
alias Grizzly.ZWave.{Command, DeviceClass}
alias Grizzly.ZWave.Commands.{
BasicReport,
ThermostatSetpointReport,
ThermostatModeReport,
ThermostatFanModeReport,
ThermostatFanStateReport,
SensorMultilevelReport,
SensorMultilevelSupportedSensorReport
}
@impl Grizzly.VirtualDevices.Device
def device_spec(_device_opts) do
DeviceClass.thermostat_hvac()
end
@doc """
Start a thermostat device
"""
@spec start_link(keyword()) :: GenServer.on_start()
def start_link(args) do
GenServer.start_link(__MODULE__, args)
end
@impl GenServer
def init(opts) do
id = VirtualDevices.add_device(__MODULE__, Keyword.merge(opts, server: self()))
state = %{
setpoints: %{
heating: 22.0,
cooling: 26.0
},
fan_mode: :auto_low,
fan_state: :off,
temperature: 24.0,
mode: :cooling,
basic: :on,
scale: :celsius,
device_id: id
}
{:ok, state}
end
@impl Grizzly.VirtualDevices.Device
def handle_command(command, device_opts) do
server = Keyword.fetch!(device_opts, :server)
GenServer.call(server, {:handle_command, command})
end
@impl GenServer
def handle_call(:get_device_id, _from, state) do
{:ok, state.device_id, state}
end
def handle_call(
{:handle_command, %Command{name: :thermostat_setpoint_get} = command},
_from,
state
) do
type = Command.param!(command, :type)
case Map.get(state.setpoints, type) do
nil ->
{:reply, {:error, :timeout}, state}
value ->
response = ThermostatSetpointReport.new(type: type, value: value, scale: state.scale)
{:reply, response, state}
end
end
def handle_call(
{:handle_command, %Command{name: :thermostat_setpoint_set} = command},
_from,
state
) do
scale = Command.param!(command, :scale)
type = Command.param!(command, :type)
value =
command
|> Command.param!(:value)
|> maybe_convert_value(scale, state)
case Map.get(state.setpoints, type) do
nil ->
{:reply, :ok, state}
_old_value ->
new_setpoints = Map.put(state.setpoints, type, value)
{:reply, :ok, %{state | setpoints: new_setpoints}}
end
end
def handle_call({:handle_command, %Command{name: :thermostat_fan_mode_get}}, _from, state) do
response = ThermostatFanModeReport.new(mode: state.fan_mode)
{:reply, response, state}
end
def handle_call({:handle_command, %Command{name: :thermostat_fan_state_get}}, _from, state) do
response = ThermostatFanStateReport.new(state: state.fan_state)
{:reply, response, state}
end
def handle_call(
{:handle_command, %Command{name: :thermostat_fan_mode_set} = command},
_from,
state
) do
mode = Command.param!(command, :mode)
{:reply, :ok, %{state | fan_mode: mode}}
end
def handle_call(
{:handle_command, %Command{name: :thermostat_fan_state_set} = command},
_from,
state
) do
fan_state = Command.param!(command, :state)
{:reply, :ok, %{state | fan_state: fan_state}}
end
def handle_call({:handle_command, %Command{name: :sensor_multilevel_get}}, _from, state) do
response =
SensorMultilevelReport.new(sensor_type: :temperature, scale: 1, value: state.temperature)
{:reply, response, state}
end
def handle_call({:handle_command, %Command{name: :thermostat_mode_get}}, _from, state) do
response = ThermostatModeReport.new(mode: state.mode)
{:reply, response, state}
end
def handle_call(
{:handle_command, %Command{name: :thermostat_mode_set} = command},
_from,
state
) do
new_mode = Command.param!(command, :mode)
{:reply, :ok, %{state | mode: new_mode}}
end
def handle_call(
{:handle_command, %Command{name: :sensor_multilevel_supported_sensor_get}},
_from,
state
) do
response = SensorMultilevelSupportedSensorReport.new(sensor_types: [:temperature])
{:reply, response, state}
end
def handle_call({:handle_command, %Command{name: :basic_get}}, _from, state) do
response = BasicReport.new(value: state.basic)
{:reply, response, state}
end
def handle_call({:handle_command, %Command{name: :basic_set} = command}, _from, state) do
value = Command.param!(command, :value)
{:reply, :ok, %{state | basic: value}}
end
def handle_call({:handle_command, _other}, state), do: {{:error, :timeout}, state}
defp maybe_convert_value(value, scale, state) do
if state.scale == scale do
value
else
convert_value(value, scale, state.scale)
end
end
defp convert_value(value, :celsius, :fahrenheit) do
celsius_to_fahrenheit(value)
end
defp convert_value(value, _fah, _cel) do
fahrenheit_to_celsius(value)
end
defp celsius_to_fahrenheit(cel) do
cel * 1.8 + 32
end
defp fahrenheit_to_celsius(fah) do
(fah - 32) / 1.8
end
end