defmodule Grizzly.VirtualDevices.Reports do @moduledoc false # Helper module for building various general reports for a virtual device alias Grizzly.Report alias Grizzly.VirtualDevices alias Grizzly.ZWave.Command alias Grizzly.ZWave.Commands alias Grizzly.ZWave.DeviceClass alias Grizzly.ZWave.DSK @doc """ Send the node add status message to the handler """ @spec send_node_add_status(VirtualDevices.device_entry(), Grizzly.handler()) :: :ok def send_node_add_status(device_entry, handler) do {handler, handler_opts} = ensure_handler_with_opts(handler) {:ok, node_add_status} = Commands.create(:node_add_status, seq_number: 0x00, node_id: device_entry.id, status: :done, listening?: true, basic_device_class: device_entry.device_class.basic_device_class, generic_device_class: device_entry.device_class.generic_device_class, specific_device_class: device_entry.device_class.specific_device_class, command_classes: command_classes_for_device(device_entry.device_class), granted_keys: [], kex_fail_type: :none, input_dsk: DSK.new("") ) report = Report.new(:complete, :command, device_entry.id, command: node_add_status) handler.handle_report(report, handler_opts) :ok end def send_node_remove_status(device_id, handler) do {handler, handler_opts} = ensure_handler_with_opts(handler) {:ok, node_remove_status} = Commands.create(:node_remove_status, seq_number: 0x01, status: :done, node_id: device_id) report = Report.new(:complete, :command, device_id, command: node_remove_status) handler.handle_report(report, handler_opts) :ok end defp ensure_handler_with_opts(handler_spec) when is_tuple(handler_spec), do: handler_spec defp ensure_handler_with_opts(handler_module) when is_atom(handler_module), do: {handler_module, []} @doc """ Build a node info cached report based of a node info get command """ @spec build_node_info_cache_report(VirtualDevices.device_entry(), Command.t()) :: Report.t() def build_node_info_cache_report(entry, node_info_get) do seq_number = Command.param!(node_info_get, :seq_number) {:ok, node_info_report} = Commands.create(:node_info_cached_report, seq_number: seq_number, status: :ok, age: 1, listening?: true, command_classes: command_classes_for_device(entry.device_class), basic_device_class: entry.device_class.basic_device_class, specific_device_class: entry.device_class.specific_device_class, generic_device_class: entry.device_class.generic_device_class ) Report.new(:complete, :command, entry.id, command: node_info_report) end @doc """ Build manufacturer specific report for the device entry """ @spec build_manufacturer_specific_report(VirtualDevices.device_entry()) :: Report.t() def build_manufacturer_specific_report(entry) do {:ok, manufacturer_report} = Commands.create(:manufacturer_specific_report, manufacturer_id: entry.device_class.manufacturer_id, product_id: entry.device_class.product_id, product_type_id: entry.device_class.product_type_id ) Report.new(:complete, :command, entry.id, command: manufacturer_report) end @doc """ Build version command class get for a command class in the entry's device class """ @spec build_version_command_class_get_report(VirtualDevices.device_entry(), Command.t()) :: Report.t() def build_version_command_class_get_report(entry, version_command_class_get) do command_class = Command.param!(version_command_class_get, :command_class) case DeviceClass.get_command_class_version(entry.device_class, command_class) do nil -> build_timeout_report(entry.id) version -> {:ok, version_report} = Commands.create(:version_command_class_report, command_class: command_class, version: version ) build_report(entry, version_report) end end @doc """ Build the association report from the association get command """ @spec build_association_report(VirtualDevices.device_entry()) :: Report.t() def build_association_report(entry) do {:ok, report} = Commands.create(:association_report, nodes: [1], grouping_identifier: 1, max_nodes_supported: 1, reports_to_follow: 0 ) build_report(entry, report) end @doc """ Build a battery report for a battery get command """ @spec build_battery_report(VirtualDevices.device_entry()) :: Report.t() def build_battery_report(entry) do {:ok, report} = Commands.create(:battery_report, level: 100) build_report(entry, report) end @doc """ Build a version report for a version get command """ @spec build_version_report(VirtualDevices.device_entry()) :: Report.t() def build_version_report(entry) do {:ok, report} = Commands.create(:version_report, library_type: entry.device_class.library_type, protocol_version: "7.16", firmware_version: "7.16", hardware_version: 1, other_firmware_versions: ["7.16"] ) build_report(entry, report) end def build_manufacturer_specific_device_specific_report(entry, command) do case Command.param!(command, :device_id_type) do :serial_number -> {:ok, report} = Commands.create(:manufacturer_specific_device_specific_report, device_id_type: :serial_number, device_id: "0000" ) build_report(entry, report) _other -> build_timeout_report(entry.id) end end @doc """ Build a Grizzly Report for a timeout """ @spec build_timeout_report(VirtualDevices.id()) :: Report.t() def build_timeout_report(device_id) do Report.new(:complete, :timeout, device_id) end @doc """ Build an ack response report """ @spec build_ack_response(VirtualDevices.device_entry()) :: Report.t() def build_ack_response(entry) do Report.new(:complete, :ack_response, entry.id) end @doc """ Build a report that contains a command """ @spec build_report(VirtualDevices.device_entry(), Command.t()) :: Report.t() def build_report(entry, command) do Report.new(:complete, :command, entry.id, command: command) end defp command_classes_for_device(device_class) do [ non_secure_supported: Map.keys(device_class.command_classes.support), non_secure_controlled: Map.keys(device_class.command_classes.control) ] end end