-module(mikutel). -behaviour(gen_server). -export([init/1, handle_cast/2, handle_call/3, handle_info/2, code_change/3, terminate/2, start_link/1]). -record(state, {socket, modulus, exponent}). %% TODO: add fallback TCP Connection start_link(Args) -> gen_server:start_link(?MODULE, Args, []). init([Host, Port, Username, Password, _]) -> Ciphers = ssl:prepend_cipher_suites([ssl:str_to_suite("AES256-GCM-SHA384")], ssl:cipher_suites(default, 'tlsv1.2')), {ok, Socket} = ssl:connect(Host, Port, [{verify, verify_none}, {versions, ['tlsv1.2']}, {ciphers, Ciphers}, {active, true}]), %% {ok, Socket} = gen_tcp:connect(Host, Port, []), %% Arbitrary sleeps because something drops my Erlang messages?? timer:send_interval(60000, ping), login(Socket, Username, Password), timer:sleep(100), set_dect_subscription_mode(Socket, 'Configured'), timer:sleep(100), subscribe(Socket, [{'PPUserCnf', [{uid, -1}]}, {'PPDevCnf', [{ppn, -1}]}]), {ok, #state{socket=Socket}}. handle_cast(_, State) -> {noreply, State}. handle_call(_E, _From, State) -> {noreply, State}. handle_cast({register, CallerID, Number}, State) -> %% TODO handle_info({subscribe, Events}, State) -> subscribe(State#state.socket, Events); handle_info({ssl, Socket, Message}, State) -> handle_response(Message, State); handle_info({ssl_closed, Socket}, State) -> ssl:close(Socket), {stop, normal, State}; %% handle_info({tcp, Socket, Message}, State) -> %% handle_response(Message, State); %% handle_info({tcp_closed, Socket}, State) -> %% gen_tcp:close(Socket), %% {stop, normal, State}; handle_info(ping, State = #state{socket=Socket}) -> Data = {'Ping', [], []}, send(Socket, Data), {noreply, State}. terminate(_Reason, _Tab) -> ok. code_change(_OldVersion, Tab, _Extra) -> {ok, Tab}. login(Socket, Username, Password) -> Data = {'Open',[{username, Username}, {password, Password}], []}, send(Socket, Data). set_dect_subscription_mode(Socket, Mode) -> Data = {'SetDECTSubscriptionMode', [{mode, Mode}], []}, send(Socket, Data). transform_event({Event, Args}) -> {e, [{cmd, 'On'}, {eventType, Event}] ++ Args, []}. subscribe(Socket, Args) -> Payload = lists:map(fun transform_event/1, Args), Data = {'Subscribe', [], Payload}, %% send(Socket, Data). send(Socket, Data). send(Socket, Data) -> RawPayload = lists:flatten(xmerl:export_simple([Data], xmerl_xml, [{prolog, ""}])), BinPayload = list_to_binary(RawPayload), Payload = binary_to_list(<>/binary>>), ssl:send(Socket, Payload). %% gen_tcp:send(Socket, Payload). handle_response(Message, State) -> {Element, _} = xmerl_scan:string(Message), {Response, Parameters, Body} = xmerl_lib:simplify_element(Element), io:format("Received: ~p~n", [Response]), io:format("Params: ~p~n", [Parameters]), io:format("Body: ~p~n", [Body]), case Response of 'OpenResp' -> handle_login(Body, Parameters, State); 'EventPPDevCnf' -> handle_device_configuration_event(Body, Parameters, State); _ -> {noreply, State} end. handle_login(_, [{publicKey, [{modulus, Modulus}, {exponent, Exponent}], []} | _], State) -> {noreply, State#state{modulus=Modulus, exponent=Exponent}}; handle_login(Body, [_ | Rest], State) -> handle_login(Body, Rest, State). handle_device_configuration_event(_, [{pp, Args, _}], State) -> handle_device_configuration_event(Args, State). handle_device_configuration_event([{ipei, Ipei} | _], State) -> ok; %% create_device(). handle_device_configuration_event([_ | Rest], State) -> handle_device_configuration_event(Rest, State).