Packages

A Gleam library to write and run Home Assistant automations.

Current section

Files

Jump to
glome src glome@homeassistant.erl
Raw

src/glome@homeassistant.erl

-module(glome@homeassistant).
-compile(no_auto_import).
-export([connect/2, add_handler/3, add_constrained_handler/4, call_service/4, get_state/2]).
-export_type([home_assistant/0, state_change_handlers_entry/0]).
-opaque home_assistant() :: {home_assistant,
list(state_change_handlers_entry()),
glome@homeassistant@environment:configuration()}.
-type state_change_handlers_entry() :: {state_change_handlers_entry,
glome@homeassistant@entity_id:entity_id(),
fun((glome@homeassistant@state_change_event:state_change_event(), home_assistant()) -> {ok,
nil} |
{error,
glome@core@error:glome_error()}),
fun((glome@homeassistant@state_change_event:state_change_event(), home_assistant()) -> boolean())}.
-spec connect(
glome@homeassistant@environment:configuration(),
fun((home_assistant()) -> home_assistant())
) -> {ok, nil} | {error, glome@core@error:glome_error()}.
connect(Config, Conn_handler) ->
Ha_api_path = case erlang:element(2, Config) of
<<"supervisor"/utf8>> ->
<<"/core/websocket"/utf8>>;
_@1 ->
<<"/api/websocket"/utf8>>
end,
{Sender, Receiver} = gleam@otp@process:new_channel(),
gleam@otp@process:start(
fun() ->
{ok, Connection@1} = case begin
_pipe = nerf@websocket:connect(
erlang:element(2, Config),
Ha_api_path,
erlang:element(3, Config),
[]
),
glome@core@error:map_connection_error(_pipe)
end of
{ok, Connection} -> {ok, Connection};
_try ->
erlang:error(#{gleam_error => assert,
message => <<"Assertion pattern match failed"/utf8>>,
value => _try,
module => <<"glome/homeassistant"/utf8>>,
function => <<"connect"/utf8>>,
line => 63})
end,
{ok, _@3} = case glome@core@authentication:authenticate(
Connection@1,
erlang:element(4, Config)
) of
{ok, _@2} -> {ok, _@2};
_try@1 ->
erlang:error(#{gleam_error => assert,
message => <<"Assertion pattern match failed"/utf8>>,
value => _try@1,
module => <<"glome/homeassistant"/utf8>>,
function => <<"connect"/utf8>>,
line => 67})
end,
{ok, _@5} = case start_state_loop(Connection@1, Sender) of
{ok, _@4} -> {ok, _@4};
_try@2 ->
erlang:error(#{gleam_error => assert,
message => <<"Assertion pattern match failed"/utf8>>,
value => _try@2,
module => <<"glome/homeassistant"/utf8>>,
function => <<"connect"/utf8>>,
line => 69})
end
end
),
Home_assistant = {home_assistant, gleam@list:new(), Config},
Handlers = erlang:element(2, Conn_handler(Home_assistant)),
glome@core@loops:start_state_change_event_receiver(
fun() ->
State_changed_event = gleam@otp@process:receive_forever(Receiver),
_pipe@1 = gleam@list:filter(
Handlers,
fun(Entry) ->
(erlang:element(2, erlang:element(2, Entry))
=:= erlang:element(
2,
erlang:element(2, State_changed_event)
))
andalso ((erlang:element(3, erlang:element(2, Entry))
=:= erlang:element(
3,
erlang:element(2, State_changed_event)
))
orelse (erlang:element(3, erlang:element(2, Entry))
=:= <<"*"/utf8>>))
end
),
_pipe@2 = gleam@list:filter(
_pipe@1,
fun(Entry@1) ->
(erlang:element(4, Entry@1))(
State_changed_event,
Home_assistant
)
end
),
gleam@list:map(
_pipe@2,
fun(Entry@2) ->
gleam@otp@process:start(
fun() ->
Result = (erlang:element(3, Entry@2))(
State_changed_event,
Home_assistant
),
case Result of
{ok, nil} ->
nil;
{error, Error} ->
gleam@io:debug(Error),
nil
end
end
)
end
),
{ok, nil}
end
),
{ok, nil}.
-spec add_handler(
home_assistant(),
glome@homeassistant@entity_id:entity_id(),
fun((glome@homeassistant@state_change_event:state_change_event(), home_assistant()) -> {ok,
nil} |
{error, glome@core@error:glome_error()})
) -> home_assistant().
add_handler(Home_assistant, Entity_id, Handler) ->
Handlers = do_add_handler_with_filter(
erlang:element(2, Home_assistant),
Entity_id,
Handler,
fun(_, _) -> true end
),
erlang:setelement(2, Home_assistant, Handlers).
-spec add_constrained_handler(
home_assistant(),
glome@homeassistant@entity_id:entity_id(),
fun((glome@homeassistant@state_change_event:state_change_event(), home_assistant()) -> {ok,
nil} |
{error, glome@core@error:glome_error()}),
fun((glome@homeassistant@state_change_event:state_change_event(), home_assistant()) -> boolean())
) -> home_assistant().
add_constrained_handler(Home_assistant, Entity_id, Handler, Filter) ->
Handlers = do_add_handler_with_filter(
erlang:element(2, Home_assistant),
Entity_id,
Handler,
Filter
),
erlang:setelement(2, Home_assistant, Handlers).
-spec call_service(
home_assistant(),
glome@homeassistant@domain:domain(),
binary(),
gleam@option:option(binary())
) -> {ok, binary()} | {error, glome@core@error:glome_error()}.
call_service(Home_assistant, Domain, Service, Service_data) ->
glome@homeassistant@service:call(
erlang:element(3, Home_assistant),
Domain,
Service,
Service_data
).
-spec get_state(home_assistant(), glome@homeassistant@entity_id:entity_id()) -> {ok,
glome@homeassistant@state:state()} |
{error, glome@core@error:glome_error()}.
get_state(Home_assistant, Entity_id) ->
glome@homeassistant@state:get(erlang:element(3, Home_assistant), Entity_id).
-spec do_add_handler_with_filter(
list(state_change_handlers_entry()),
glome@homeassistant@entity_id:entity_id(),
fun((glome@homeassistant@state_change_event:state_change_event(), home_assistant()) -> {ok,
nil} |
{error, glome@core@error:glome_error()}),
fun((glome@homeassistant@state_change_event:state_change_event(), home_assistant()) -> boolean())
) -> list(state_change_handlers_entry()).
do_add_handler_with_filter(Handlers, Entity_id, Handler, Filter) ->
gleam@list:append(
Handlers,
[{state_change_handlers_entry, Entity_id, Handler, Filter}]
).
-spec start_state_loop(
nerf@websocket:connection(),
gleam@otp@process:sender(glome@homeassistant@state_change_event:state_change_event())
) -> {ok, nil} | {error, glome@core@error:glome_error()}.
start_state_loop(Connection, Sender) ->
Subscribe_state_change_events = glome@core@json:encode(
[glome@core@json:json_element(<<"id"/utf8>>, <<"1"/utf8>>),
glome@core@json:json_element(
<<"type"/utf8>>,
<<"subscribe_events"/utf8>>
),
glome@core@json:json_element(
<<"event_type"/utf8>>,
<<"state_changed"/utf8>>
)]
),
nerf@websocket:send(Connection, Subscribe_state_change_events),
glome@core@loops:start_state_change_event_publisher(
fun() -> case nerf@websocket:'receive'(Connection, 500) of
{ok, {text, Message}} ->
publish_state_change_event(Sender, Message);
{error, Nil} ->
{error, loop_nil}
end end
),
{ok, nil}.
-spec publish_state_change_event(
gleam@otp@process:sender(glome@homeassistant@state_change_event:state_change_event()),
binary()
) -> {ok, nil} | {error, glome@core@error:glome_error()}.
publish_state_change_event(Sender, Message) ->
case glome@homeassistant@state_change_event:from_json(Message) of
{error, _try} -> {error, _try};
{ok, State_change_event} ->
gleam@otp@process:send(Sender, State_change_event),
{ok, nil}
end.