181 lines
5.3 KiB
Nix
181 lines
5.3 KiB
Nix
|
{ lib, config, pkgs, ... }:
|
||
|
let
|
||
|
|
||
|
folderPath = config.services.home-assistant.configDir;
|
||
|
|
||
|
# find symbols with
|
||
|
# https://www.alphavantage.co/query?function=SYMBOL_SEARCH&keywords=<keywords>&apikey=<api_key>
|
||
|
# as described here : https://www.alphavantage.co/documentation/#symbolsearch
|
||
|
#
|
||
|
# example:
|
||
|
# --------
|
||
|
# stocks = [
|
||
|
# {
|
||
|
# symbol = "GOOGL";
|
||
|
# name = "google";
|
||
|
# friendly_name = "Google";
|
||
|
# currency = "$";
|
||
|
# # I own 50 and bought at a price of 1000
|
||
|
# own = {
|
||
|
# pieces = 50;
|
||
|
# price = 1000;
|
||
|
# };
|
||
|
# }
|
||
|
# ];
|
||
|
stocks = import <secrets/home-assistant/stocks>;
|
||
|
filePath = name: "${folderPath}/stock_${name}.json";
|
||
|
|
||
|
cleanup_list = list: lib.filter (entry: entry != { }) (lib.flatten list);
|
||
|
|
||
|
in {
|
||
|
services.homeAssistantConfig = {
|
||
|
|
||
|
sensor = cleanup_list (map ({ name, currency, own ? { }, ... }: [
|
||
|
{
|
||
|
platform = "file";
|
||
|
name = "stock_${name}";
|
||
|
file_path = filePath name;
|
||
|
value_template = "{{ value_json.price}} ${currency}";
|
||
|
|
||
|
}
|
||
|
{
|
||
|
platform = "file";
|
||
|
name = "stock_${name}_change";
|
||
|
file_path = filePath name;
|
||
|
value_template = "{{ value_json.change}} ${currency}";
|
||
|
|
||
|
}
|
||
|
{
|
||
|
platform = "file";
|
||
|
name = "stock_${name}_change_percent";
|
||
|
file_path = filePath name;
|
||
|
value_template = "{{ value_json.change_percent}} %";
|
||
|
}
|
||
|
(lib.optionalAttrs (own != { }) {
|
||
|
platform = "file";
|
||
|
name = "stock_${name}_profit";
|
||
|
file_path = filePath name;
|
||
|
value_template = ''
|
||
|
{{ "{:,.2f}".format( value_json.price * ${toString own.pieces} - ${
|
||
|
toString (own.pieces * own.price)
|
||
|
} ) }} ${currency}'';
|
||
|
})
|
||
|
]) stocks);
|
||
|
|
||
|
homeassistant = {
|
||
|
whitelist_external_dirs = [ folderPath ];
|
||
|
customize = builtins.listToAttrs (cleanup_list (map
|
||
|
({ name, own ? { }, ... }: [
|
||
|
{
|
||
|
name = "sensor.stock_${name}";
|
||
|
value = {
|
||
|
icon = "mdi:cash-usd-outline";
|
||
|
friendly_name = "Price";
|
||
|
};
|
||
|
}
|
||
|
{
|
||
|
name = "sensor.stock_${name}_change";
|
||
|
value = {
|
||
|
icon = "mdi:radar";
|
||
|
friendly_name = "Difference";
|
||
|
};
|
||
|
}
|
||
|
{
|
||
|
name = "sensor.stock_${name}_change_percent";
|
||
|
value = {
|
||
|
icon = "mdi:radar";
|
||
|
friendly_name = "Percent";
|
||
|
};
|
||
|
}
|
||
|
(lib.optionalAttrs (own != { }) {
|
||
|
name = "sensor.stock_${name}_profit";
|
||
|
value = {
|
||
|
icon = "mdi:radar";
|
||
|
friendly_name = "Profit";
|
||
|
};
|
||
|
})
|
||
|
]) stocks));
|
||
|
};
|
||
|
|
||
|
group = {
|
||
|
"stocks" = {
|
||
|
name = "Aktien";
|
||
|
view = true;
|
||
|
control = "hidden";
|
||
|
entities = map ({ name, ... }: "group.stock_${name}") stocks;
|
||
|
};
|
||
|
} // (builtins.listToAttrs (map ({ name, friendly_name, own ? { }, ... }: {
|
||
|
name = "stock_${name}";
|
||
|
value = {
|
||
|
name = friendly_name;
|
||
|
control = "hidden";
|
||
|
entities = [
|
||
|
"sensor.stock_${name}"
|
||
|
"sensor.stock_${name}_change"
|
||
|
"sensor.stock_${name}_change_percent"
|
||
|
] ++ (lib.optional (own != { }) "sensor.stock_${name}_profit");
|
||
|
};
|
||
|
}) stocks));
|
||
|
|
||
|
};
|
||
|
|
||
|
systemd.services = let
|
||
|
pullService = { name, symbol, currency, ... }: {
|
||
|
name = "pull_stock_${name}";
|
||
|
value = {
|
||
|
enable = true;
|
||
|
before = [ "home-assistant.service" ];
|
||
|
wantedBy = [ "home-assistant.service" ];
|
||
|
serviceConfig = {
|
||
|
User = "hass";
|
||
|
Type = "oneshot";
|
||
|
};
|
||
|
description = "pull stock_${name} for homeassistant";
|
||
|
script = ''
|
||
|
SYMBOL="${symbol}"
|
||
|
CURRENCY="${currency}"
|
||
|
APIKEY=${
|
||
|
lib.fileContents <secrets/home-assistant/alphavantage/apikey>
|
||
|
}
|
||
|
|
||
|
${pkgs.curl}/bin/curl --location --silent \
|
||
|
"https://www.alphavantage.co/query?function=GLOBAL_QUOTE&symbol=$SYMBOL&apikey=$APIKEY" \
|
||
|
| ${pkgs.jq}/bin/jq --compact-output \
|
||
|
'.["Global Quote"] |
|
||
|
{
|
||
|
price: .["05. price"] | tonumber,
|
||
|
currency: "'$CURRENCY'",
|
||
|
change_percent: .["10. change percent"] | .[0:-1] | tonumber,
|
||
|
change: .["09. change"] | tonumber,
|
||
|
last_date: .["07. latest trading day"],
|
||
|
}' \
|
||
|
>> ${filePath name}
|
||
|
|
||
|
# old and stupid
|
||
|
#${pkgs.curl}/bin/curl --location --silent \
|
||
|
#"https://www.alphavantage.co/query?function=TIME_SERIES_INTRADAY&symbol=$SYMBOL&interval=5min&apikey=$APIKEY" \
|
||
|
#| ${pkgs.jq}/bin/jq --compact-output \
|
||
|
# '.["Time Series (5min)"] | to_entries | [ .[]
|
||
|
# | { date : .key , value : .value["4. close"], currency: "'$CURRENCY'" } ]
|
||
|
# | sort_by(.date) | reverse | .[0]' \
|
||
|
'';
|
||
|
};
|
||
|
};
|
||
|
in builtins.listToAttrs (map pullService stocks);
|
||
|
|
||
|
systemd.timers = let
|
||
|
pullTimer = { name, ... }: {
|
||
|
name = "pull_stock_${name}";
|
||
|
value = {
|
||
|
enable = true;
|
||
|
wantedBy = [ "multi-user.target" ];
|
||
|
timerConfig = {
|
||
|
OnCalendar = "hourly";
|
||
|
Persistent = "true";
|
||
|
};
|
||
|
};
|
||
|
};
|
||
|
in builtins.listToAttrs (map pullTimer stocks);
|
||
|
|
||
|
}
|