Packages
grizzly
8.2.3
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/unsolicited_server/socket.ex
defmodule Grizzly.UnsolicitedServer.Socket do
@moduledoc false
use GenServer
require Logger
alias Grizzly.{Report, SeqNumber, Transport, ZWave}
alias Grizzly.ZWave.Command
alias Grizzly.ZWave.Commands.ZIPPacket
alias Grizzly.UnsolicitedServer.{Messages, ResponseHandler, SocketSupervisor}
@spec child_spec(Transport.t()) :: map()
def child_spec(transport) do
%{
id: __MODULE__,
start: {__MODULE__, :start_link, [transport]},
restart: :temporary
}
end
@spec start_link(Transport.t()) :: GenServer.on_start()
def start_link(listening_transport) do
GenServer.start_link(__MODULE__, listening_transport)
end
@impl GenServer
def init(transport) do
{:ok, transport, {:continue, :accept}}
end
@impl GenServer
def handle_continue(:accept, listening_transport) do
with {:ok, accept_transport} <- Transport.accept(listening_transport),
{:ok, transport} <- Transport.handshake(accept_transport) do
{:ok, node_id} = Transport.node_id(transport)
Logger.metadata(node_id: node_id)
# Start a new listen socket to replace this one as this one is now bound
# to a single node in the Z-Wave PAN.
{:ok, _} = SocketSupervisor.start_socket(listening_transport)
{:noreply, transport}
else
other ->
Logger.warning(
"[Grizzly] UnsolicitedServer socket accept/handshake failed: #{inspect(other)}"
)
{:stop, :handshake_error, listening_transport}
end
end
@impl GenServer
def handle_info({:ssl_closed, sslsocket}, transport) do
ip = client_ip(sslsocket)
Logger.debug("[Grizzly] UnsolicitedServer socket closed by client #{ip}")
{:stop, :normal, transport}
end
def handle_info({:grizzly, :binary_response, _does_not_matter}, transport) do
{:noreply, transport}
end
def handle_info(response, transport) do
case Transport.parse_response(transport, response) do
{:ok, :connection_closed} ->
{:stop, :normal, transport}
{:ok, transport_response} ->
actions = [:ack | ResponseHandler.handle_response(transport_response)]
Enum.each(actions, &run_response_action(transport_response, &1, transport))
{:noreply, transport}
end
end
defp run_response_action(response, :ack, transport) do
%Transport.Response{command: zippacket} = response
_ = send_ack_response(zippacket, transport)
end
defp run_response_action(response, {:send, command}, transport) do
%Transport.Response{command: zippacket} = response
# We have to preserve the header extensions to ensure command encapsulation
# is correct when sending the response back to the Z-Wave PAN.
header_extensions = Command.param!(zippacket, :header_extensions)
{:ok, zip_packet} =
ZIPPacket.with_zwave_command(command, SeqNumber.get_and_inc(),
flag: :ack_request,
header_extensions: header_extensions
)
binary = ZWave.to_binary(zip_packet)
Transport.send(transport, binary)
end
defp run_response_action(_response, {:send_raw, command}, transport) do
binary = ZWave.to_binary(command)
Transport.send(transport, binary)
end
defp run_response_action(response, {:notify, command}, _transport) do
:ok = Messages.broadcast(response.ip_address, command)
end
defp run_response_action(_response, {:forward_to_controller, command}, transport) do
case Grizzly.send_command(:gateway, command.name, command.params) do
{:ok, report} ->
handle_grizzly_report(report, transport)
error ->
error
end
end
defp handle_grizzly_report(%Report{type: :ack_response}, transport) do
zip_packet = ZIPPacket.make_ack_response(SeqNumber.get_and_inc())
binary = ZWave.to_binary(zip_packet)
Transport.send(transport, binary)
end
defp handle_grizzly_report(%Report{type: :command, command: command}, transport) do
{:ok, zip_packet} = ZIPPacket.with_zwave_command(command, SeqNumber.get_and_inc(), flag: nil)
binary = ZWave.to_binary(zip_packet)
Transport.send(transport, binary)
end
defp send_ack_response(zippacket, transport) do
header_extensions = Command.param!(zippacket, :header_extensions)
seq = Command.param!(zippacket, :seq_number)
more_info = more_info?(zippacket)
if more_info do
Logger.debug("[Grizzly] Adding More Information flag to ACK response")
end
ack_bin =
seq
|> ZIPPacket.make_ack_response(
header_extensions: header_extensions,
more_info: more_info
)
|> ZWave.to_binary()
Transport.send(transport, ack_bin)
end
defp more_info?(zip_packet) do
encapsulated_command = Command.param(zip_packet, :command)
if encapsulated_command && encapsulated_command.name == :supervision_get do
true
else
false
end
end
defp client_ip(sslsocket) do
with {:ok, {ip, _port}} <- :ssl.peername(sslsocket),
ip_str when is_list(ip_str) <- :inet.ntoa(ip) do
to_string(ip_str)
else
_ ->
"unknown"
end
end
end