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_selector:entity_selector(),
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 => 54})
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 => 58})
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 => 59})
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@3 = gleam@list:filter(
Handlers,
fun(Entry) ->
(erlang:element(2, erlang:element(2, Entry))
=:= erlang:element(
2,
erlang:element(2, State_changed_event)
))
andalso case erlang:element(3, erlang:element(2, Entry)) of
{object_id, Object_id} ->
Object_id =:= erlang:element(
3,
erlang:element(2, State_changed_event)
);
all ->
true;
{regex, Pattern} ->
_pipe@1 = gleam@regex:from_string(Pattern),
_pipe@2 = gleam@result:map(
_pipe@1,
fun(_capture) ->
gleam@regex:check(
_capture,
erlang:element(
3,
erlang:element(
2,
State_changed_event
)
)
)
end
),
gleam@result:unwrap(_pipe@2, false)
end
end
),
_pipe@4 = gleam@list:filter(
_pipe@3,
fun(Entry@1) ->
(erlang:element(4, Entry@1))(
State_changed_event,
Home_assistant
)
end
),
gleam@list:map(
_pipe@4,
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_selector:entity_selector(),
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_selector, Handler) ->
Handlers = do_add_handler_with_filter(
erlang:element(2, Home_assistant),
Entity_selector,
Handler,
fun(_, _) -> true end
),
erlang:setelement(2, Home_assistant, Handlers).
-spec add_constrained_handler(
home_assistant(),
glome@homeassistant@entity_selector:entity_selector(),
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_selector, Handler, Filter) ->
Handlers = do_add_handler_with_filter(
erlang:element(2, Home_assistant),
Entity_selector,
Handler,
Filter
),
erlang:setelement(2, Home_assistant, Handlers).
-spec call_service(
home_assistant(),
glome@homeassistant@domain:domain(),
glome@homeassistant@service:service(),
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_selector:entity_selector(),
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_selector, Handler, Filter) ->
gleam@list:append(
Handlers,
[{state_change_handlers_entry, Entity_selector, 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 = gleam@json:to_string(
gleam_json_ffi:object(
[{<<"id"/utf8>>, gleam_json_ffi:string(<<"1"/utf8>>)},
{<<"type"/utf8>>,
gleam_json_ffi:string(<<"subscribe_events"/utf8>>)},
{<<"event_type"/utf8>>,
gleam_json_ffi:string(<<"state_changed"/utf8>>)}]
)
),
nerf@websocket:send(Connection, Subscribe_state_change_events),
case nerf@websocket:'receive'(Connection, 500) of
{ok, {text, Message}} ->
{ok, gleam@io:debug(Message)};
{error, _@1} ->
{error, loop_nil}
end,
glome@core@loops:start_state_change_event_publisher(
fun() -> case nerf@websocket:'receive'(Connection, 500) of
{ok, {text, Message@1}} ->
publish_state_change_event(Sender, Message@1);
{error, _@2} ->
{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:decode(Message) of
{error, _try} -> {error, _try};
{ok, State_change_event} ->
gleam@otp@process:send(Sender, State_change_event),
{ok, nil}
end.