Compare commits
1
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
d08915b541 |
No files matched your search
@@ -2,7 +2,6 @@ _final: prev:
|
||||
(import ./abseil { inherit prev; })
|
||||
// {
|
||||
gnutls = import ./gnutls { inherit (prev) gnutls; };
|
||||
libjpeg_turbo = import ./libjpeg-turbo { inherit (prev) lib libjpeg_turbo stdenv; };
|
||||
prometheus = import ./prometheus { inherit (prev) prometheus; };
|
||||
zopfli = import ./zopfli { inherit (prev) zopfli; };
|
||||
|
||||
|
||||
@@ -1,15 +0,0 @@
|
||||
{
|
||||
lib,
|
||||
libjpeg_turbo,
|
||||
stdenv,
|
||||
}:
|
||||
libjpeg_turbo.overrideAttrs (old: {
|
||||
# This overlay also reaches pkgsi686Linux. Its 12-bit floating-point code
|
||||
# uses x87, while x86-64-v3 uses FMA. Keep the tests enabled for both.
|
||||
cmakeFlags =
|
||||
(old.cmakeFlags or [ ])
|
||||
++ lib.optional (
|
||||
stdenv.hostPlatform.isx86_64 && (stdenv.hostPlatform.gcc.arch or null) == "x86-64-v3"
|
||||
) "-DFLOATTEST12=fp-contract"
|
||||
++ lib.optional stdenv.hostPlatform.isi686 "-DFLOATTEST12=387";
|
||||
})
|
||||
Generated
+21
-21
@@ -7,11 +7,11 @@
|
||||
]
|
||||
},
|
||||
"locked": {
|
||||
"lastModified": 1792526520,
|
||||
"narHash": "sha256-3hSJlv8mUVnZPMWK2+CM7a9O+nR6Z4mCh27NfLCmTPc=",
|
||||
"lastModified": 1789770686,
|
||||
"narHash": "sha256-uZkBR7yHdIKUFB5SZdfgh1qkGfI3XmYmI/lTiquxbck=",
|
||||
"owner": "nix-community",
|
||||
"repo": "disko",
|
||||
"rev": "cedd626e11b635470fa6f9fdb8ad79b0075dd7f9",
|
||||
"rev": "725ea35e410ad83be4931d1bff7e090eacaf3563",
|
||||
"type": "github"
|
||||
},
|
||||
"original": {
|
||||
@@ -45,11 +45,11 @@
|
||||
]
|
||||
},
|
||||
"locked": {
|
||||
"lastModified": 1793193205,
|
||||
"narHash": "sha256-X6xa6sGA7RnJQuZGbFMC+K+KToWAlBDCCjJXfTwEPsw=",
|
||||
"lastModified": 1791293500,
|
||||
"narHash": "sha256-3hwJd2/vG/VYV1P/g7oDgdbNsWODhN5F//Od+hPMm2k=",
|
||||
"owner": "nix-community",
|
||||
"repo": "home-manager",
|
||||
"rev": "095fef2f265c3c0da4fe315efba8f8a04495ab34",
|
||||
"rev": "0e11b7bcba1d74ef8ea10bfdfb1e1a5811a21087",
|
||||
"type": "github"
|
||||
},
|
||||
"original": {
|
||||
@@ -67,11 +67,11 @@
|
||||
"rust-overlay": "rust-overlay"
|
||||
},
|
||||
"locked": {
|
||||
"lastModified": 1793017781,
|
||||
"narHash": "sha256-YRem1WRn//ruQ7QukBCjpn/Uwmd17vGLml8w3i+Swd8=",
|
||||
"lastModified": 1791127101,
|
||||
"narHash": "sha256-SmrBBhcv9iNHA2vlafi7Zpe8bkcsMjVNSmCCVf5/jrQ=",
|
||||
"owner": "LLukas22",
|
||||
"repo": "Jellyswarrm",
|
||||
"rev": "90cca4fe7e6f855453866b71ca5b38773342a0c4",
|
||||
"rev": "0b4301c6c74efff9c3634296913032579c98faf6",
|
||||
"type": "github"
|
||||
},
|
||||
"original": {
|
||||
@@ -85,11 +85,11 @@
|
||||
"nixpkgs": "nixpkgs"
|
||||
},
|
||||
"locked": {
|
||||
"lastModified": 1793236063,
|
||||
"narHash": "sha256-jGFe4xdrCw6xpgH2zgKwslACyt/NGuPwGILPEMFxmcQ=",
|
||||
"lastModified": 1791128201,
|
||||
"narHash": "sha256-rlsYY/Dg/LhbIj51Aa6TLvRpZeb3KZgUTd7llQ5XcJE=",
|
||||
"owner": "nixos",
|
||||
"repo": "nixos-hardware",
|
||||
"rev": "b6754c901c21478fd8450d62101e712b2a4f0d75",
|
||||
"rev": "31cc5f4d9b9ba601071e8b8504601b9b176e2756",
|
||||
"type": "github"
|
||||
},
|
||||
"original": {
|
||||
@@ -114,11 +114,11 @@
|
||||
},
|
||||
"nixpkgs-master": {
|
||||
"locked": {
|
||||
"lastModified": 1793315900,
|
||||
"narHash": "sha256-z5lZoCwheBzMgRtfYlGLMhmPwhv1Rty16Kf269n8ctc=",
|
||||
"lastModified": 1791296764,
|
||||
"narHash": "sha256-N2b6Uxdr1MFhscEPqDyntTTtGZASgq3XYE0ujRzfeNI=",
|
||||
"owner": "nixos",
|
||||
"repo": "nixpkgs",
|
||||
"rev": "db379a58acfef9783b46ffde2957dac397c92b85",
|
||||
"rev": "a15afac8691bd24f746255ffbd76306693e4cf7a",
|
||||
"type": "github"
|
||||
},
|
||||
"original": {
|
||||
@@ -146,11 +146,11 @@
|
||||
},
|
||||
"nixpkgs_2": {
|
||||
"locked": {
|
||||
"lastModified": 1793110635,
|
||||
"narHash": "sha256-dhoW6VgFWlaodSY5jrBn+CF2FLmaUvDVgM7+C7T2+NM=",
|
||||
"lastModified": 1791260994,
|
||||
"narHash": "sha256-Miqqk/ammqnTUxaoCyvtwoPeLbI1ksFyLxi/CazZpWY=",
|
||||
"owner": "nixos",
|
||||
"repo": "nixpkgs",
|
||||
"rev": "31ecbaca1bdefa45dffc409f0250bd8039ea0010",
|
||||
"rev": "151fa4e8ddfdd8dd25d945ad94ed54a13de9f6e4",
|
||||
"type": "github"
|
||||
},
|
||||
"original": {
|
||||
@@ -201,11 +201,11 @@
|
||||
]
|
||||
},
|
||||
"locked": {
|
||||
"lastModified": 1793052255,
|
||||
"narHash": "sha256-Y/JIHTnMlOabDfyaSBubWGLDIVGihLEUuoMhFvQKaDo=",
|
||||
"lastModified": 1791103873,
|
||||
"narHash": "sha256-nFxM+pKoZ8LJAEnUXARyCaOAloWgaW9kZQOSjWzKcTE=",
|
||||
"owner": "Mic92",
|
||||
"repo": "sops-nix",
|
||||
"rev": "60182817ee048ac94b4c68ea76d5ef2e8aac730d",
|
||||
"rev": "dcd241ba97088c22569d1573286e1b9daad340c0",
|
||||
"type": "github"
|
||||
},
|
||||
"original": {
|
||||
|
||||
@@ -5,13 +5,11 @@
|
||||
extra-substituters = [
|
||||
"https://cache.nixos.org/?priority=2&want-mass-query=true"
|
||||
"https://nix-community.cachix.org/?priority=10&want-mass-query=true"
|
||||
"https://attic.nayeonie.com/nix-cache/?priority=10&want-mass-query=true"
|
||||
];
|
||||
extra-trusted-public-keys = [
|
||||
"cache.nixos.org-1:6NCHdD59X431o0gWypbMrAURkbJ16ZPMQFGspcDShjY=" # cspell:disable-line
|
||||
"nix-community.cachix.org-1:mB9FSh9qf2dCimDSUo8Zy7bkq5CX+/rkCWyvRCYg3Fs=" # cspell:disable-line
|
||||
"cache-nix-dot:Od9KN34LXc6Lu7y1ozzV1kIXZa8coClozgth/SYE7dU=" # cspell:disable-line
|
||||
"nix-cache:grGRsHhqNDhkEuTODvHJXYmoCClntC+U8XAJQzwMaZM=" # cspell:disable-line
|
||||
];
|
||||
};
|
||||
|
||||
|
||||
@@ -82,8 +82,6 @@
|
||||
extraComponents = [
|
||||
"isal"
|
||||
"modbus" # for victron modbus integration
|
||||
"template" # MultiPlus mode selector, input current limit and derived sensors
|
||||
"wled"
|
||||
];
|
||||
customComponents = with pkgs.x86-v1.home-assistant-custom-components; [
|
||||
garmin_connect
|
||||
|
||||
@@ -0,0 +1,122 @@
|
||||
title: Van Overview
|
||||
views:
|
||||
- title: Overview
|
||||
path: overview
|
||||
type: sections
|
||||
sections:
|
||||
# Battery Status Section
|
||||
- type: grid
|
||||
title: Battery Status
|
||||
cards:
|
||||
- type: gauge
|
||||
entity: sensor.jk0_soc
|
||||
name: Battery 0
|
||||
min: 0
|
||||
max: 100
|
||||
severity:
|
||||
green: 50
|
||||
yellow: 20
|
||||
red: 0
|
||||
needle: true
|
||||
|
||||
- type: gauge
|
||||
entity: sensor.jk1_soc
|
||||
name: Battery 1
|
||||
min: 0
|
||||
max: 100
|
||||
severity:
|
||||
green: 50
|
||||
yellow: 20
|
||||
red: 0
|
||||
needle: true
|
||||
|
||||
- type: entity
|
||||
entity: sensor.jk0_total_voltage
|
||||
name: Batt 0 Voltage
|
||||
|
||||
- type: entity
|
||||
entity: sensor.jk1_total_voltage
|
||||
name: Batt 1 Voltage
|
||||
|
||||
# Solar Production Section
|
||||
- type: grid
|
||||
title: Solar Production
|
||||
cards:
|
||||
- type: gauge
|
||||
entity: sensor.solar_wattage
|
||||
name: Solar Now
|
||||
min: 0
|
||||
max: 400
|
||||
severity:
|
||||
green: 100
|
||||
yellow: 50
|
||||
red: 0
|
||||
needle: true
|
||||
|
||||
- type: entity
|
||||
entity: sensor.solar_yield_today
|
||||
name: Today's Yield
|
||||
|
||||
- type: entity
|
||||
entity: sensor.estimated_energy_production_today
|
||||
name: Forecast Today
|
||||
|
||||
- type: entity
|
||||
entity: sensor.estimated_energy_production_tomorrow
|
||||
name: Forecast Tomorrow
|
||||
|
||||
# Environment Section
|
||||
- type: grid
|
||||
title: Van Environment
|
||||
cards:
|
||||
- type: entity
|
||||
entity: sensor.environment_temperature
|
||||
name: Inside Temp
|
||||
|
||||
- type: entity
|
||||
entity: sensor.environment_humidity
|
||||
name: Inside Humidity
|
||||
|
||||
- type: entity
|
||||
entity: sensor.current_weather_temperature
|
||||
name: Outside Temp
|
||||
|
||||
- type: entity
|
||||
entity: sensor.current_weather_humidity
|
||||
name: Outside Humidity
|
||||
|
||||
- type: entity
|
||||
entity: sensor.current_weather_condition
|
||||
name: Weather
|
||||
|
||||
# Charger Status Section
|
||||
- type: grid
|
||||
title: Charger
|
||||
cards:
|
||||
- type: entity
|
||||
entity: sensor.charger_dc_wattage
|
||||
name: Charging Power
|
||||
|
||||
- type: entity
|
||||
entity: sensor.charger_charge_state
|
||||
name: Charge State
|
||||
|
||||
- type: entity
|
||||
entity: sensor.charger_dc_wattage_daily
|
||||
name: Charged Today
|
||||
|
||||
# Location Section
|
||||
- type: grid
|
||||
title: Location
|
||||
cards:
|
||||
- type: entity
|
||||
entity: sensor.gps_location
|
||||
name: Current Location
|
||||
|
||||
- type: entity
|
||||
entity: sensor.gps_speed
|
||||
name: Speed
|
||||
|
||||
- type: entity
|
||||
entity: binary_sensor.gps_fix_available
|
||||
name: GPS Fix
|
||||
@@ -220,120 +220,10 @@ modbus:
|
||||
state_class: measurement
|
||||
unique_id: gps_altitude_id_1
|
||||
|
||||
# ---- MULTIPLUS-II (VE.Bus, Cerbo GX unit ID 227) ----
|
||||
# Confirmed on this GX: one AC phase, AC input 1, 30 A shore-power plug.
|
||||
# Register map: https://www.victronenergy.com/upload/documents/CCGX-Modbus-TCP-register-list-3.81.xlsx
|
||||
# Victron's scale column is a divisor: AC power registers use 0.1,
|
||||
# so Home Assistant must multiply their signed raw values by 10.
|
||||
# Change the unit ID anchor here if the GX service mapping changes.
|
||||
- name: MultiPlus AC Input Voltage
|
||||
unique_id: multiplus_ac_input_voltage
|
||||
slave: &multiplus_unit_id 227
|
||||
address: 3
|
||||
input_type: holding
|
||||
data_type: uint16
|
||||
scale: 0.1
|
||||
precision: 1
|
||||
unit_of_measurement: "V"
|
||||
device_class: voltage
|
||||
state_class: measurement
|
||||
scan_interval: 10
|
||||
|
||||
- name: MultiPlus AC Input Current
|
||||
unique_id: multiplus_ac_input_current
|
||||
slave: *multiplus_unit_id
|
||||
address: 6
|
||||
input_type: holding
|
||||
data_type: int16
|
||||
scale: 0.1
|
||||
precision: 1
|
||||
unit_of_measurement: "A"
|
||||
device_class: current
|
||||
state_class: measurement
|
||||
scan_interval: 10
|
||||
|
||||
- name: MultiPlus AC Input Frequency
|
||||
unique_id: multiplus_ac_input_frequency
|
||||
slave: *multiplus_unit_id
|
||||
address: 9
|
||||
input_type: holding
|
||||
data_type: int16
|
||||
scale: 0.01
|
||||
precision: 2
|
||||
unit_of_measurement: "Hz"
|
||||
device_class: frequency
|
||||
state_class: measurement
|
||||
scan_interval: 10
|
||||
|
||||
- name: MultiPlus AC Input Power
|
||||
unique_id: multiplus_ac_input_power
|
||||
slave: *multiplus_unit_id
|
||||
address: 12
|
||||
input_type: holding
|
||||
data_type: int16
|
||||
scale: 10
|
||||
precision: 0
|
||||
unit_of_measurement: "W"
|
||||
device_class: power
|
||||
state_class: measurement
|
||||
scan_interval: 10
|
||||
|
||||
- name: MultiPlus AC Output Voltage
|
||||
unique_id: multiplus_ac_output_voltage
|
||||
slave: *multiplus_unit_id
|
||||
address: 15
|
||||
input_type: holding
|
||||
data_type: uint16
|
||||
scale: 0.1
|
||||
precision: 1
|
||||
unit_of_measurement: "V"
|
||||
device_class: voltage
|
||||
state_class: measurement
|
||||
scan_interval: 10
|
||||
|
||||
- name: MultiPlus AC Output Current
|
||||
unique_id: multiplus_ac_output_current
|
||||
slave: *multiplus_unit_id
|
||||
address: 18
|
||||
input_type: holding
|
||||
data_type: int16
|
||||
scale: 0.1
|
||||
precision: 1
|
||||
unit_of_measurement: "A"
|
||||
device_class: current
|
||||
state_class: measurement
|
||||
scan_interval: 10
|
||||
|
||||
- name: MultiPlus AC Output Frequency
|
||||
unique_id: multiplus_ac_output_frequency
|
||||
slave: *multiplus_unit_id
|
||||
address: 21
|
||||
input_type: holding
|
||||
data_type: int16
|
||||
scale: 0.01
|
||||
precision: 2
|
||||
unit_of_measurement: "Hz"
|
||||
device_class: frequency
|
||||
state_class: measurement
|
||||
scan_interval: 10
|
||||
|
||||
- name: MultiPlus AC Output Power
|
||||
unique_id: multiplus_ac_output_power
|
||||
slave: *multiplus_unit_id
|
||||
address: 23
|
||||
input_type: holding
|
||||
data_type: int16
|
||||
scale: 10
|
||||
precision: 0
|
||||
unit_of_measurement: "W"
|
||||
device_class: power
|
||||
state_class: measurement
|
||||
scan_interval: 10
|
||||
|
||||
- name: MultiPlus Battery Voltage
|
||||
unique_id: multiplus_battery_voltage
|
||||
slave: *multiplus_unit_id
|
||||
address: 26
|
||||
# ---- CHARGER (Unit ID 223) ----
|
||||
- name: Charger Output 1 Voltage
|
||||
slave: 223
|
||||
address: 2307
|
||||
input_type: holding
|
||||
data_type: uint16
|
||||
scale: 0.01
|
||||
@@ -341,12 +231,11 @@ modbus:
|
||||
unit_of_measurement: "V"
|
||||
device_class: voltage
|
||||
state_class: measurement
|
||||
scan_interval: 10
|
||||
unique_id: charger_output_1_voltage
|
||||
|
||||
- name: MultiPlus Battery Current
|
||||
unique_id: multiplus_battery_current
|
||||
slave: *multiplus_unit_id
|
||||
address: 27
|
||||
- name: Charger Output 1 Current
|
||||
slave: 223
|
||||
address: 2308
|
||||
input_type: holding
|
||||
data_type: int16
|
||||
scale: 0.1
|
||||
@@ -354,12 +243,11 @@ modbus:
|
||||
unit_of_measurement: "A"
|
||||
device_class: current
|
||||
state_class: measurement
|
||||
scan_interval: 10
|
||||
unique_id: charger_output_1_current
|
||||
|
||||
- name: MultiPlus Battery Temperature
|
||||
unique_id: multiplus_battery_temperature
|
||||
slave: *multiplus_unit_id
|
||||
address: 61
|
||||
- name: Charger Output 1 Temperature
|
||||
slave: 223
|
||||
address: 2309
|
||||
input_type: holding
|
||||
data_type: int16
|
||||
scale: 0.1
|
||||
@@ -367,23 +255,11 @@ modbus:
|
||||
unit_of_measurement: "°C"
|
||||
device_class: temperature
|
||||
state_class: measurement
|
||||
unique_id: charger_output_1_temperature
|
||||
|
||||
- name: MultiPlus State of Charge
|
||||
unique_id: multiplus_state_of_charge
|
||||
slave: *multiplus_unit_id
|
||||
address: 30
|
||||
input_type: holding
|
||||
data_type: uint16
|
||||
scale: 0.1
|
||||
precision: 1
|
||||
unit_of_measurement: "%"
|
||||
device_class: battery
|
||||
state_class: measurement
|
||||
|
||||
- name: MultiPlus AC Input Current Limit Readback
|
||||
unique_id: multiplus_ac_input_current_limit_readback
|
||||
slave: *multiplus_unit_id
|
||||
address: 22
|
||||
- name: Charger AC Current
|
||||
slave: 223
|
||||
address: 2314
|
||||
input_type: holding
|
||||
data_type: int16
|
||||
scale: 0.1
|
||||
@@ -391,272 +267,73 @@ modbus:
|
||||
unit_of_measurement: "A"
|
||||
device_class: current
|
||||
state_class: measurement
|
||||
scan_interval: 10
|
||||
unique_id: charger_ac_current
|
||||
|
||||
- name: MultiPlus Mode Raw
|
||||
unique_id: multiplus_mode_raw
|
||||
slave: *multiplus_unit_id
|
||||
address: 33
|
||||
- name: Charger AC Current Limit
|
||||
slave: 223
|
||||
address: 2316
|
||||
input_type: holding
|
||||
data_type: uint16
|
||||
scan_interval: 10
|
||||
|
||||
- name: MultiPlus State Raw
|
||||
unique_id: multiplus_state_raw
|
||||
slave: *multiplus_unit_id
|
||||
address: 31
|
||||
input_type: holding
|
||||
data_type: uint16
|
||||
scan_interval: 10
|
||||
|
||||
- name: MultiPlus Charge State Raw
|
||||
unique_id: multiplus_charge_state_raw
|
||||
slave: *multiplus_unit_id
|
||||
address: 95
|
||||
input_type: holding
|
||||
data_type: uint16
|
||||
scan_interval: 10
|
||||
|
||||
- name: MultiPlus Active Input Raw
|
||||
unique_id: multiplus_active_input_raw
|
||||
slave: *multiplus_unit_id
|
||||
address: 29
|
||||
input_type: holding
|
||||
data_type: uint16
|
||||
scan_interval: 10
|
||||
|
||||
- name: MultiPlus Phase Count
|
||||
unique_id: multiplus_phase_count
|
||||
slave: *multiplus_unit_id
|
||||
address: 28
|
||||
input_type: holding
|
||||
data_type: uint16
|
||||
|
||||
- name: MultiPlus VE Bus Error
|
||||
unique_id: multiplus_ve_bus_error
|
||||
slave: *multiplus_unit_id
|
||||
address: 32
|
||||
input_type: holding
|
||||
data_type: uint16
|
||||
|
||||
- name: MultiPlus High Temperature Alarm Raw
|
||||
unique_id: multiplus_high_temperature_alarm_raw
|
||||
slave: *multiplus_unit_id
|
||||
address: 34
|
||||
input_type: holding
|
||||
data_type: uint16
|
||||
|
||||
- name: MultiPlus Low Battery Alarm Raw
|
||||
unique_id: multiplus_low_battery_alarm_raw
|
||||
slave: *multiplus_unit_id
|
||||
address: 35
|
||||
input_type: holding
|
||||
data_type: uint16
|
||||
|
||||
- name: MultiPlus Overload Alarm Raw
|
||||
unique_id: multiplus_overload_alarm_raw
|
||||
slave: *multiplus_unit_id
|
||||
address: 36
|
||||
input_type: holding
|
||||
data_type: uint16
|
||||
|
||||
- name: MultiPlus Ripple Alarm Raw
|
||||
unique_id: multiplus_ripple_alarm_raw
|
||||
slave: *multiplus_unit_id
|
||||
address: 47
|
||||
input_type: holding
|
||||
data_type: uint16
|
||||
|
||||
- name: MultiPlus Temperature Sensor Alarm Raw
|
||||
unique_id: multiplus_temperature_sensor_alarm_raw
|
||||
slave: *multiplus_unit_id
|
||||
address: 42
|
||||
input_type: holding
|
||||
data_type: uint16
|
||||
|
||||
- name: MultiPlus Voltage Sensor Alarm Raw
|
||||
unique_id: multiplus_voltage_sensor_alarm_raw
|
||||
slave: *multiplus_unit_id
|
||||
address: 43
|
||||
input_type: holding
|
||||
data_type: uint16
|
||||
|
||||
- name: MultiPlus Grid Lost Alarm Raw
|
||||
unique_id: multiplus_grid_lost_alarm_raw
|
||||
slave: *multiplus_unit_id
|
||||
address: 64
|
||||
input_type: holding
|
||||
data_type: uint16
|
||||
|
||||
- name: MultiPlus BMS Pre Alarm Raw
|
||||
unique_id: multiplus_bms_pre_alarm_raw
|
||||
slave: *multiplus_unit_id
|
||||
address: 94
|
||||
input_type: holding
|
||||
data_type: uint16
|
||||
|
||||
- name: MultiPlus BMS Allows Charging Raw
|
||||
unique_id: multiplus_bms_allows_charging_raw
|
||||
slave: *multiplus_unit_id
|
||||
address: 57
|
||||
input_type: holding
|
||||
data_type: uint16
|
||||
|
||||
- name: MultiPlus BMS Allows Discharging Raw
|
||||
unique_id: multiplus_bms_allows_discharging_raw
|
||||
slave: *multiplus_unit_id
|
||||
address: 58
|
||||
input_type: holding
|
||||
data_type: uint16
|
||||
|
||||
- name: MultiPlus BMS Expected Raw
|
||||
unique_id: multiplus_bms_expected_raw
|
||||
slave: *multiplus_unit_id
|
||||
address: 59
|
||||
input_type: holding
|
||||
data_type: uint16
|
||||
|
||||
- name: MultiPlus BMS Error
|
||||
unique_id: multiplus_bms_error
|
||||
slave: *multiplus_unit_id
|
||||
address: 60
|
||||
input_type: holding
|
||||
data_type: uint16
|
||||
|
||||
- name: MultiPlus AC Input Ignored Raw
|
||||
unique_id: multiplus_ac_input_ignored_raw
|
||||
slave: *multiplus_unit_id
|
||||
address: 69
|
||||
input_type: holding
|
||||
data_type: uint16
|
||||
|
||||
- name: MultiPlus AC Input to Output Energy
|
||||
unique_id: multiplus_ac_input_to_output_energy
|
||||
slave: *multiplus_unit_id
|
||||
address: 74
|
||||
input_type: holding
|
||||
data_type: uint32
|
||||
scale: 0.01
|
||||
precision: 2
|
||||
unit_of_measurement: "kWh"
|
||||
device_class: energy
|
||||
state_class: total_increasing
|
||||
# Hardware counters reset on Multi/GX restart; total_increasing handles this.
|
||||
|
||||
- name: MultiPlus AC Input to Battery Energy
|
||||
unique_id: multiplus_ac_input_to_battery_energy
|
||||
slave: *multiplus_unit_id
|
||||
address: 76
|
||||
input_type: holding
|
||||
data_type: uint32
|
||||
scale: 0.01
|
||||
precision: 2
|
||||
unit_of_measurement: "kWh"
|
||||
device_class: energy
|
||||
state_class: total_increasing
|
||||
|
||||
- name: MultiPlus AC Output to Input Energy
|
||||
unique_id: multiplus_ac_output_to_input_energy
|
||||
slave: *multiplus_unit_id
|
||||
address: 82
|
||||
input_type: holding
|
||||
data_type: uint32
|
||||
scale: 0.01
|
||||
precision: 2
|
||||
unit_of_measurement: "kWh"
|
||||
device_class: energy
|
||||
state_class: total_increasing
|
||||
|
||||
- name: MultiPlus Battery to AC Input Energy
|
||||
unique_id: multiplus_battery_to_ac_input_energy
|
||||
slave: *multiplus_unit_id
|
||||
address: 86
|
||||
input_type: holding
|
||||
data_type: uint32
|
||||
scale: 0.01
|
||||
precision: 2
|
||||
unit_of_measurement: "kWh"
|
||||
device_class: energy
|
||||
state_class: total_increasing
|
||||
|
||||
- name: MultiPlus Battery to AC Output Energy
|
||||
unique_id: multiplus_battery_to_ac_output_energy
|
||||
slave: *multiplus_unit_id
|
||||
address: 90
|
||||
input_type: holding
|
||||
data_type: uint32
|
||||
scale: 0.01
|
||||
precision: 2
|
||||
unit_of_measurement: "kWh"
|
||||
device_class: energy
|
||||
state_class: total_increasing
|
||||
|
||||
- name: MultiPlus AC Output to Battery Energy
|
||||
unique_id: multiplus_ac_output_to_battery_energy
|
||||
slave: *multiplus_unit_id
|
||||
address: 92
|
||||
input_type: holding
|
||||
data_type: uint32
|
||||
scale: 0.01
|
||||
precision: 2
|
||||
unit_of_measurement: "kWh"
|
||||
device_class: energy
|
||||
state_class: total_increasing
|
||||
|
||||
- name: MultiPlus PowerAssist Boost Factor
|
||||
unique_id: multiplus_powerassist_boost_factor
|
||||
slave: *multiplus_unit_id
|
||||
address: 106
|
||||
input_type: holding
|
||||
data_type: uint16
|
||||
scale: 0.01
|
||||
precision: 2
|
||||
data_type: int16
|
||||
scale: 0.1
|
||||
precision: 1
|
||||
unit_of_measurement: "A"
|
||||
device_class: current
|
||||
state_class: measurement
|
||||
unique_id: charger_ac_current_limit
|
||||
|
||||
- name: MultiPlus Configured Output Voltage
|
||||
unique_id: multiplus_configured_output_voltage
|
||||
slave: *multiplus_unit_id
|
||||
address: 107
|
||||
- name: Charger On Off Raw
|
||||
slave: 223
|
||||
address: 2317
|
||||
input_type: holding
|
||||
data_type: uint16
|
||||
scale: 0.01
|
||||
precision: 2
|
||||
unit_of_measurement: "V"
|
||||
device_class: voltage
|
||||
state_class: measurement
|
||||
|
||||
- name: MultiPlus PowerAssist Enabled Raw
|
||||
unique_id: multiplus_powerassist_enabled_raw
|
||||
slave: *multiplus_unit_id
|
||||
address: 108
|
||||
input_type: holding
|
||||
data_type: uint16
|
||||
|
||||
- name: MultiPlus UPS Function Raw
|
||||
unique_id: multiplus_ups_function_raw
|
||||
slave: *multiplus_unit_id
|
||||
address: 109
|
||||
input_type: holding
|
||||
data_type: uint16
|
||||
|
||||
- name: MultiPlus Weak AC Input Raw
|
||||
unique_id: multiplus_weak_ac_input_raw
|
||||
slave: *multiplus_unit_id
|
||||
address: 129
|
||||
input_type: holding
|
||||
data_type: uint16
|
||||
|
||||
- name: MultiPlus Nominal Inverter Power
|
||||
unique_id: multiplus_nominal_inverter_power
|
||||
slave: *multiplus_unit_id
|
||||
address: 130
|
||||
input_type: holding
|
||||
data_type: uint32
|
||||
scale: 1
|
||||
precision: 0
|
||||
unit_of_measurement: "W"
|
||||
device_class: power
|
||||
state_class: measurement
|
||||
unique_id: charger_on_off_raw
|
||||
|
||||
- name: Charger Charge State Raw
|
||||
slave: 223
|
||||
address: 2318
|
||||
input_type: holding
|
||||
data_type: uint16
|
||||
scale: 1
|
||||
state_class: measurement
|
||||
unique_id: charger_charge_state_raw
|
||||
|
||||
- name: Charger Error Code
|
||||
slave: 223
|
||||
address: 2319
|
||||
input_type: holding
|
||||
data_type: uint16
|
||||
scale: 1
|
||||
state_class: measurement
|
||||
unique_id: charger_error_code
|
||||
|
||||
- name: Charger Relay State
|
||||
slave: 223
|
||||
address: 2320
|
||||
input_type: holding
|
||||
data_type: uint16
|
||||
scale: 1
|
||||
state_class: measurement
|
||||
unique_id: charger_relay_state
|
||||
|
||||
- name: Charger Low Voltage Alarm
|
||||
slave: 223
|
||||
address: 2321
|
||||
input_type: holding
|
||||
data_type: uint16
|
||||
scale: 1
|
||||
state_class: measurement
|
||||
unique_id: charger_low_voltage_alarm
|
||||
|
||||
- name: Charger High Voltage Alarm
|
||||
slave: 223
|
||||
address: 2322
|
||||
input_type: holding
|
||||
data_type: uint16
|
||||
scale: 1
|
||||
state_class: measurement
|
||||
unique_id: charger_high_voltage_alarm
|
||||
|
||||
template:
|
||||
- sensor:
|
||||
@@ -762,208 +439,34 @@ template:
|
||||
{{ v100 }}
|
||||
{% endif %}
|
||||
|
||||
- name: MultiPlus State
|
||||
unique_id: multiplus_state
|
||||
availability: "{{ is_number(states('sensor.multiplus_state_raw')) }}"
|
||||
- name: Charger On Off
|
||||
state: >-
|
||||
{% set v = states('sensor.multiplus_state_raw') | int(-1) %}
|
||||
{{ {
|
||||
0:'Off',1:'Low power',2:'Fault',3:'Bulk',4:'Absorption',5:'Float',
|
||||
6:'Storage',7:'Equalize',8:'Pass-through',9:'Inverting',
|
||||
10:'PowerAssist',11:'Power supply',244:'Sustain',252:'External control'
|
||||
}.get(v, 'Unknown') }}
|
||||
{% set v = states('sensor.charger_on_off_raw')|int %}
|
||||
{{ {0:'Off',1:'On',2:'Error',3:'Unavailable'}.get(v, 'Unknown') }}
|
||||
|
||||
# Keep the existing charger entities and energy history.
|
||||
- name: Charger Charge State
|
||||
availability: "{{ is_number(states('sensor.multiplus_charge_state_raw')) }}"
|
||||
state: >-
|
||||
{% set v = states('sensor.multiplus_charge_state_raw') | int(-1) %}
|
||||
{% set v = states('sensor.charger_charge_state_raw')|int %}
|
||||
{{ {
|
||||
0:'Initialising',1:'Bulk',2:'Absorption',3:'Float',4:'Storage',
|
||||
5:'Absorption repeat',6:'Forced absorption',7:'Equalize',
|
||||
8:'Bulk stopped',9:'Unknown'
|
||||
}.get(v, 'Unknown') }}
|
||||
|
||||
- name: MultiPlus Active Input
|
||||
unique_id: multiplus_active_input
|
||||
availability: "{{ is_number(states('sensor.multiplus_active_input_raw')) }}"
|
||||
state: >-
|
||||
{{ {0:'AC input 1',1:'AC input 2',240:'Disconnected'}
|
||||
.get(states('sensor.multiplus_active_input_raw') | int(-1), 'Unknown') }}
|
||||
|
||||
# VE.Bus DC current is positive while charging and negative while inverting.
|
||||
- name: MultiPlus DC Power
|
||||
unique_id: multiplus_dc_power
|
||||
unit_of_measurement: "W"
|
||||
device_class: power
|
||||
state_class: measurement
|
||||
availability: >-
|
||||
{{ is_number(states('sensor.multiplus_battery_voltage'))
|
||||
and is_number(states('sensor.multiplus_battery_current')) }}
|
||||
state: >-
|
||||
{{ (states('sensor.multiplus_battery_voltage') | float(0)
|
||||
* states('sensor.multiplus_battery_current') | float(0)) | round(1) }}
|
||||
0:'Off',1:'Low Power',2:'Fault',3:'Bulk',4:'Absorption',5:'Float',
|
||||
6:'Storage',7:'Equalize/Manual',8:'External Control'
|
||||
}.get(v,'Unknown') }}
|
||||
|
||||
- name: "Charger DC Wattage"
|
||||
unique_id: charger_dc_wattage
|
||||
unit_of_measurement: "W"
|
||||
device_class: power
|
||||
state_class: measurement
|
||||
availability: "{{ is_number(states('sensor.multiplus_dc_power')) }}"
|
||||
state: >-
|
||||
{{ [states('sensor.multiplus_dc_power') | float(0), 0] | max }}
|
||||
|
||||
- name: MultiPlus Inverter DC Power
|
||||
unique_id: multiplus_inverter_dc_power
|
||||
unit_of_measurement: "W"
|
||||
device_class: power
|
||||
state_class: measurement
|
||||
availability: "{{ is_number(states('sensor.multiplus_dc_power')) }}"
|
||||
state: >-
|
||||
{{ [0 - states('sensor.multiplus_dc_power') | float(0), 0] | max }}
|
||||
{% set v = states('sensor.charger_output_1_voltage')|float(0) %}
|
||||
{% set a = states('sensor.charger_output_1_current')|float(0) %}
|
||||
{{ (v * a) | round(1) }}
|
||||
|
||||
- binary_sensor:
|
||||
- name: MultiPlus AC Input Connected
|
||||
unique_id: multiplus_ac_input_connected
|
||||
device_class: power
|
||||
# Register 29 identifies the selected input; it alone does not establish
|
||||
# that the Multi is using AC. Keep the entity ID, but report active use.
|
||||
availability: >-
|
||||
{{ states('sensor.multiplus_active_input_raw') | int(-1) in [0, 1, 240]
|
||||
and states('sensor.multiplus_mode_raw') | int(-1) in [1, 2, 3, 4]
|
||||
and states('sensor.multiplus_state_raw') | int(-1)
|
||||
in [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 244, 252] }}
|
||||
state: >-
|
||||
{{ states('sensor.multiplus_active_input_raw') | int(-1) in [0, 1]
|
||||
and states('sensor.multiplus_mode_raw') | int(-1) in [1, 2, 3]
|
||||
and states('sensor.multiplus_state_raw') | int(-1)
|
||||
in [3, 4, 5, 6, 7, 8, 10, 11, 244, 252] }}
|
||||
|
||||
- name: MultiPlus High Temperature Alarm
|
||||
unique_id: multiplus_high_temperature_alarm
|
||||
device_class: problem
|
||||
availability: >-
|
||||
{{ states('sensor.multiplus_high_temperature_alarm_raw') | int(-1) in [0, 1, 2] }}
|
||||
state: "{{ states('sensor.multiplus_high_temperature_alarm_raw') | int(0) > 0 }}"
|
||||
|
||||
- name: MultiPlus Low Battery Alarm
|
||||
unique_id: multiplus_low_battery_alarm
|
||||
device_class: problem
|
||||
availability: >-
|
||||
{{ states('sensor.multiplus_low_battery_alarm_raw') | int(-1) in [0, 1, 2] }}
|
||||
state: "{{ states('sensor.multiplus_low_battery_alarm_raw') | int(0) > 0 }}"
|
||||
|
||||
- name: MultiPlus Overload Alarm
|
||||
unique_id: multiplus_overload_alarm
|
||||
device_class: problem
|
||||
availability: >-
|
||||
{{ states('sensor.multiplus_overload_alarm_raw') | int(-1) in [0, 1, 2] }}
|
||||
state: "{{ states('sensor.multiplus_overload_alarm_raw') | int(0) > 0 }}"
|
||||
|
||||
- name: MultiPlus Ripple Alarm
|
||||
unique_id: multiplus_ripple_alarm
|
||||
device_class: problem
|
||||
availability: >-
|
||||
{{ states('sensor.multiplus_ripple_alarm_raw') | int(-1) in [0, 1, 2] }}
|
||||
state: "{{ states('sensor.multiplus_ripple_alarm_raw') | int(0) > 0 }}"
|
||||
|
||||
- name: MultiPlus Temperature Sensor Alarm
|
||||
unique_id: multiplus_temperature_sensor_alarm
|
||||
device_class: problem
|
||||
availability: >-
|
||||
{{ states('sensor.multiplus_temperature_sensor_alarm_raw') | int(-1) in [0, 1, 2] }}
|
||||
state: "{{ states('sensor.multiplus_temperature_sensor_alarm_raw') | int(0) > 0 }}"
|
||||
|
||||
- name: MultiPlus Voltage Sensor Alarm
|
||||
unique_id: multiplus_voltage_sensor_alarm
|
||||
device_class: problem
|
||||
availability: >-
|
||||
{{ states('sensor.multiplus_voltage_sensor_alarm_raw') | int(-1) in [0, 1, 2] }}
|
||||
state: "{{ states('sensor.multiplus_voltage_sensor_alarm_raw') | int(0) > 0 }}"
|
||||
|
||||
- name: MultiPlus Grid Lost Alarm
|
||||
unique_id: multiplus_grid_lost_alarm
|
||||
device_class: problem
|
||||
availability: >-
|
||||
{{ states('sensor.multiplus_grid_lost_alarm_raw') | int(-1) in [0, 2] }}
|
||||
state: "{{ is_state('sensor.multiplus_grid_lost_alarm_raw', '2') }}"
|
||||
|
||||
- name: MultiPlus PowerAssist Enabled
|
||||
unique_id: multiplus_powerassist_enabled
|
||||
availability: >-
|
||||
{{ states('sensor.multiplus_powerassist_enabled_raw') | int(-1) in [0, 1] }}
|
||||
state: "{{ is_state('sensor.multiplus_powerassist_enabled_raw', '1') }}"
|
||||
|
||||
- name: MultiPlus UPS Enabled
|
||||
unique_id: multiplus_ups_enabled
|
||||
availability: >-
|
||||
{{ states('sensor.multiplus_ups_function_raw') | int(-1) in [0, 1] }}
|
||||
# Victron register 109 uses 0 for enabled and 1 for disabled.
|
||||
state: "{{ is_state('sensor.multiplus_ups_function_raw', '0') }}"
|
||||
|
||||
# These controls read their state from the Multi; they never write on startup.
|
||||
# GX/Digital Multi Control/VE.Bus BMS settings can restrict remote control.
|
||||
- select:
|
||||
- name: MultiPlus Mode
|
||||
unique_id: multiplus_mode
|
||||
icon: mdi:power-settings
|
||||
availability: >-
|
||||
{{ states('sensor.multiplus_mode_raw') | int(-1) in [1, 2, 3, 4] }}
|
||||
state: >-
|
||||
{{ {1:'Charger only',2:'Inverter only',3:'On',4:'Off'}
|
||||
.get(states('sensor.multiplus_mode_raw') | int(-1), 'Unknown') }}
|
||||
options: "{{ ['On', 'Charger only', 'Inverter only', 'Off'] }}"
|
||||
select_option:
|
||||
- condition: template
|
||||
value_template: >-
|
||||
{{ states('sensor.multiplus_mode_raw') | int(-1) in [1, 2, 3, 4]
|
||||
and option in ['On', 'Charger only', 'Inverter only', 'Off'] }}
|
||||
- action: modbus.write_register
|
||||
data:
|
||||
hub: victron_gx
|
||||
slave: *multiplus_unit_id
|
||||
address: 33
|
||||
value: "{{ {'Charger only':1,'Inverter only':2,'On':3,'Off':4}[option] }}"
|
||||
- delay:
|
||||
seconds: 2
|
||||
- action: homeassistant.update_entity
|
||||
target:
|
||||
entity_id:
|
||||
- sensor.multiplus_mode_raw
|
||||
- sensor.multiplus_state_raw
|
||||
- sensor.multiplus_active_input_raw
|
||||
|
||||
- number:
|
||||
- name: MultiPlus AC Input Current Limit
|
||||
unique_id: multiplus_ac_input_current_limit
|
||||
icon: mdi:current-ac
|
||||
device_class: current
|
||||
unit_of_measurement: "A"
|
||||
min: 0
|
||||
# Van shore-power plug rating; this is a UI bound, not a startup setting.
|
||||
max: 30
|
||||
step: 0.1
|
||||
availability: >-
|
||||
{{ is_number(states('sensor.multiplus_ac_input_current_limit_readback'))
|
||||
and states('sensor.multiplus_ac_input_current_limit_readback') | float(-1) >= 0 }}
|
||||
state: "{{ states('sensor.multiplus_ac_input_current_limit_readback') | float(0) }}"
|
||||
set_value:
|
||||
- condition: template
|
||||
value_template: >-
|
||||
{{ is_number(states('sensor.multiplus_ac_input_current_limit_readback'))
|
||||
and is_number(value) and 0 <= value | float(-1) <= 30 }}
|
||||
- action: modbus.write_register
|
||||
data:
|
||||
hub: victron_gx
|
||||
slave: *multiplus_unit_id
|
||||
address: 22
|
||||
# Register 22 is in tenths of an amp: 10.8 A -> 108.
|
||||
value: "{{ (value | float * 10) | round(0) | int }}"
|
||||
- delay:
|
||||
seconds: 2
|
||||
- action: homeassistant.update_entity
|
||||
target:
|
||||
entity_id: sensor.multiplus_ac_input_current_limit_readback
|
||||
- name: Charger Low Voltage Alarm Active
|
||||
state: "{{ states('sensor.charger_low_voltage_alarm')|int == 2 }}"
|
||||
- name: Charger High Voltage Alarm Active
|
||||
state: "{{ states('sensor.charger_high_voltage_alarm')|int == 2 }}"
|
||||
|
||||
sensor:
|
||||
- platform: integration
|
||||
@@ -993,16 +496,6 @@ sensor:
|
||||
max_sub_interval:
|
||||
minutes: 5
|
||||
|
||||
- platform: integration
|
||||
source: sensor.multiplus_inverter_dc_power
|
||||
name: MultiPlus Inverter DC Energy
|
||||
unique_id: multiplus_inverter_dc_energy
|
||||
unit_prefix: k
|
||||
round: 2
|
||||
method: trapezoidal
|
||||
max_sub_interval:
|
||||
minutes: 5
|
||||
|
||||
utility_meter:
|
||||
dc_load_energy_daily:
|
||||
source: sensor.dc_system_energy
|
||||
@@ -1027,11 +520,3 @@ utility_meter:
|
||||
charger_dc_wattage_monthly:
|
||||
source: sensor.dc_charger_energy
|
||||
cycle: monthly
|
||||
|
||||
multiplus_inverter_dc_energy_daily:
|
||||
source: sensor.multiplus_inverter_dc_energy
|
||||
cycle: daily
|
||||
|
||||
multiplus_inverter_dc_energy_monthly:
|
||||
source: sensor.multiplus_inverter_dc_energy
|
||||
cycle: monthly
|
||||
@@ -0,0 +1,96 @@
|
||||
# Kanidm on Jeeves
|
||||
|
||||
Kanidm is served at **https://auth.tmmworkshop.com**. Portal terminates public
|
||||
HTTPS and forwards requests over Tailscale to Jeeves on port 8443, verifying
|
||||
Jeeves's TLS certificate. Only the Tailscale interface exposes that backend port.
|
||||
|
||||
The service module is automatically imported by `services/default.nix`.
|
||||
`services/kanidm.nix` pins Kanidm to the 1.11 release series; review the
|
||||
[upgrade guide](https://kanidm.github.io/kanidm/stable/server_updates.html)
|
||||
before changing that series. The flake lock determines the patch release.
|
||||
|
||||
## Before deployment
|
||||
|
||||
1. Point `auth.tmmworkshop.com` at Portal's public address, using the same DNS
|
||||
arrangement as `gitea.tmmworkshop.com`. Port 80 must reach Portal for ACME.
|
||||
2. Enable [HTTPS certificates in Tailscale](https://tailscale.com/kb/1153/enabling-https).
|
||||
Jeeves must be able to obtain a certificate for `jeeves.taile39c31.ts.net`.
|
||||
3. Allow Portal to reach Jeeves on TCP 8443 in your Tailscale policy.
|
||||
|
||||
The trusted proxy address in `services/kanidm.nix` is Portal's current Tailscale
|
||||
IPv4 address, `100.80.102.33`. Update it if Portal is re-enrolled with a new address.
|
||||
HAProxy connects over IPv4 and replaces incoming `X-Forwarded-For` headers.
|
||||
|
||||
## Deploy
|
||||
|
||||
Ensure the new Nix file is tracked by Git before using this Git-backed flake.
|
||||
From this repository, using the existing SSH aliases:
|
||||
|
||||
```sh
|
||||
nixos-rebuild switch --flake .#jeeves --target-host jeeves --sudo
|
||||
nixos-rebuild switch --flake .#portal-1 --target-host portal-1 --sudo
|
||||
```
|
||||
|
||||
Deploy Jeeves first, then Portal. Portal manages the public Let's Encrypt
|
||||
certificate. `kanidm-tls.service` on Jeeves obtains the backend certificate and
|
||||
checks for renewal daily, restarting Kanidm only when the certificate changes.
|
||||
Private keys are generated at runtime and never stored in this repository or
|
||||
the Nix store.
|
||||
|
||||
On Jeeves, check startup:
|
||||
|
||||
```sh
|
||||
sudo systemctl status kanidm-tls.service kanidm.service
|
||||
sudo journalctl -u kanidm-tls.service -u kanidm.service -n 100
|
||||
```
|
||||
|
||||
If the initial certificate request fails, fix the Tailscale HTTPS configuration
|
||||
and run `sudo systemctl restart kanidm.service`; its dependency retries the
|
||||
certificate request. A successful oneshot certificate service normally appears
|
||||
as inactive after finishing.
|
||||
|
||||
After both hosts are deployed:
|
||||
|
||||
```sh
|
||||
curl --fail https://auth.tmmworkshop.com/status
|
||||
```
|
||||
|
||||
## First login
|
||||
|
||||
On Jeeves, recover the initial administrator passwords interactively:
|
||||
|
||||
```sh
|
||||
sudo kanidmd recover-account admin
|
||||
sudo kanidmd recover-account idm_admin
|
||||
kanidm login --name idm_admin
|
||||
kanidm person create richie "Richie"
|
||||
kanidm person credential create-reset-token richie
|
||||
```
|
||||
|
||||
Store the recovered passwords securely. Follow the reset URL to enroll your
|
||||
personal account's passkey. `admin` manages the Kanidm server; `idm_admin`
|
||||
manages people, groups, and application integrations. Creating `richie` does
|
||||
not automatically grant administrator rights. The client is installed and
|
||||
configured to use `https://auth.tmmworkshop.com`.
|
||||
|
||||
Register each website as its own OAuth2/OIDC client and each backend identity
|
||||
as a service account, then grant application scopes through groups. This
|
||||
deployment does not create application clients or change existing sites' login
|
||||
settings. See the [OAuth2 guide](https://kanidm.github.io/kanidm/stable/integrations/oauth2.html)
|
||||
and [service-account guide](https://kanidm.github.io/kanidm/stable/accounts/service_accounts.html).
|
||||
|
||||
## Storage and recovery
|
||||
|
||||
- Database: `/var/lib/kanidm/kanidm.db` on Jeeves's existing ZFS `/var` filesystem.
|
||||
- Online backups: `/var/lib/kanidm/backups`, daily at 02:30, retaining 14.
|
||||
- Backend certificate and key: `/var/lib/kanidm-tls`, readable by Kanidm.
|
||||
- Server configuration: `/etc/kanidm/server.toml`.
|
||||
|
||||
The existing `root_pool/var` snapshot policy covers the database and backups.
|
||||
Copy backups off Jeeves as part of disaster recovery; local snapshots do not
|
||||
protect against loss of the host. Follow Kanidm's
|
||||
[backup and restore instructions](https://kanidm.github.io/kanidm/stable/backup_and_restore.html).
|
||||
|
||||
The configured identity domain and origin are both `auth.tmmworkshop.com`.
|
||||
Changing them after enrollment can affect passkeys and application integrations;
|
||||
follow the [domain rename guide](https://kanidm.github.io/kanidm/stable/domain_rename.html).
|
||||
@@ -8,6 +8,7 @@ let
|
||||
grafana = 3000;
|
||||
jellyfin_http = 8096;
|
||||
jellyswarrm = 3100;
|
||||
kanidm = 8443;
|
||||
nix_binary_cache = 5000;
|
||||
ollama = 11434;
|
||||
open_webui = 8080;
|
||||
@@ -28,6 +29,7 @@ in
|
||||
ports.gems
|
||||
ports.jellyfin_http
|
||||
ports.jellyswarrm
|
||||
ports.kanidm
|
||||
ports.nix_binary_cache
|
||||
ports.share
|
||||
];
|
||||
|
||||
@@ -1,38 +1,21 @@
|
||||
#!/usr/bin/env bash
|
||||
|
||||
# Newer runner modules export INSTANCE_DIR and multiple colon-separated state paths.
|
||||
instance_dir="${INSTANCE_DIR:-${STATE_DIRECTORY%%:*}/$1}"
|
||||
legacy_instance_dir="$(dirname "$instance_dir")/$(hostname)"
|
||||
legacy_instance_dir="$STATE_DIRECTORY/$(hostname)"
|
||||
instance_dir="$STATE_DIRECTORY/$1"
|
||||
runner_url="$2"
|
||||
runner_config_file="$3"
|
||||
shift 3
|
||||
runner_labels="$(IFS=,; printf '%s' "$*")"
|
||||
|
||||
if [ "$legacy_instance_dir" != "$instance_dir" ] && [ -d "$legacy_instance_dir" ]; then
|
||||
# systemd may have already created an empty instance directory.
|
||||
if [ ! -e "$instance_dir" ] || rmdir "$instance_dir" 2>/dev/null; then
|
||||
mv "$legacy_instance_dir" "$instance_dir"
|
||||
fi
|
||||
if [ ! -e "$instance_dir" ] && [ -d "$legacy_instance_dir" ]; then
|
||||
mv "$legacy_instance_dir" "$instance_dir"
|
||||
fi
|
||||
|
||||
mkdir -vp "$instance_dir"
|
||||
cd "$instance_dir" || exit 1
|
||||
|
||||
# Newer modules use LoadCredential; older ones export TOKEN via EnvironmentFile.
|
||||
runner_token="${TOKEN:-}"
|
||||
if [ -n "${CREDENTIALS_DIRECTORY:-}" ]; then
|
||||
runner_token="$(cat "$CREDENTIALS_DIRECTORY/token")"
|
||||
# Keep accepting the legacy TOKEN= format as well as a raw token.
|
||||
while IFS= read -r token_line; do
|
||||
if [[ "$token_line" =~ ^[[:space:]]*TOKEN= ]]; then
|
||||
runner_token="${token_line#*=}"
|
||||
fi
|
||||
done <<< "$runner_token"
|
||||
fi
|
||||
: "${runner_token:?Runner registration token is missing}"
|
||||
|
||||
token_hash_file="$instance_dir/.token-hash"
|
||||
token_hash_current="$(printf '%s' "$runner_token" | sha256sum | cut -d' ' -f1)"
|
||||
token_hash_current="$(printf '%s' "$TOKEN" | sha256sum | cut -d' ' -f1)"
|
||||
token_hash_stored="$(cat "$token_hash_file" 2>/dev/null || echo "")"
|
||||
labels_file="$instance_dir/.labels"
|
||||
labels_wanted="$(printf '%s\n' "$@" | sort)"
|
||||
@@ -43,7 +26,7 @@ if [ ! -e "$instance_dir/.runner" ] || [ "$labels_wanted" != "$labels_current" ]
|
||||
|
||||
gitea-runner register --no-interactive \
|
||||
--instance "$runner_url" \
|
||||
--token "$runner_token" \
|
||||
--token "$TOKEN" \
|
||||
--name "jeeves-$(hostname)" \
|
||||
--labels "$runner_labels" \
|
||||
--config "$runner_config_file"
|
||||
|
||||
@@ -0,0 +1,101 @@
|
||||
{ config, pkgs, ... }:
|
||||
let
|
||||
certificateDir = "/var/lib/kanidm-tls";
|
||||
in
|
||||
{
|
||||
services.kanidm = {
|
||||
# Pin the minor release: Kanidm upgrades require following its upgrade guide.
|
||||
package = pkgs.kanidm_1_11;
|
||||
server = {
|
||||
enable = true;
|
||||
settings = {
|
||||
domain = "auth.tmmworkshop.com";
|
||||
origin = "https://auth.tmmworkshop.com";
|
||||
bindaddress = "0.0.0.0:8443";
|
||||
db_fs_type = "zfs";
|
||||
tls_chain = "${certificateDir}/certificate.pem";
|
||||
tls_key = "${certificateDir}/private-key.pem";
|
||||
|
||||
# Only Portal may supply the original client address. HAProxy replaces
|
||||
# X-Forwarded-For, so clients cannot supply a forged address themselves.
|
||||
http_client_address_info.x-forward-for = [ "100.80.102.33" ];
|
||||
|
||||
# /var/lib/kanidm is on root_pool/var, which is already snapshotted.
|
||||
online_backup = {
|
||||
schedule = "30 2 * * *";
|
||||
versions = 14;
|
||||
};
|
||||
};
|
||||
};
|
||||
client = {
|
||||
enable = true;
|
||||
settings.uri = "https://auth.tmmworkshop.com";
|
||||
};
|
||||
};
|
||||
|
||||
systemd = {
|
||||
services = {
|
||||
kanidm = {
|
||||
requires = [ "kanidm-tls.service" ];
|
||||
after = [ "kanidm-tls.service" ];
|
||||
};
|
||||
|
||||
# Kanidm requires TLS even behind HAProxy. Use Jeeves's Tailscale
|
||||
# certificate so Portal can verify both the CA and the backend hostname.
|
||||
# HTTPS certificates must be enabled in the Tailscale admin console.
|
||||
kanidm-tls = {
|
||||
description = "Obtain and renew Kanidm's backend TLS certificate";
|
||||
requires = [ "tailscaled.service" ];
|
||||
wants = [ "tailscaled-autoconnect.service" ];
|
||||
after = [
|
||||
"tailscaled.service"
|
||||
"tailscaled-autoconnect.service"
|
||||
];
|
||||
path = [
|
||||
config.services.tailscale.package
|
||||
pkgs.coreutils
|
||||
pkgs.systemd
|
||||
];
|
||||
serviceConfig = {
|
||||
Type = "oneshot";
|
||||
User = "root";
|
||||
Group = "kanidm";
|
||||
StateDirectory = "kanidm-tls";
|
||||
StateDirectoryMode = "0750";
|
||||
RuntimeDirectory = "kanidm-tls";
|
||||
RuntimeDirectoryMode = "0700";
|
||||
UMask = "0077";
|
||||
TimeoutStartSec = "2min";
|
||||
};
|
||||
script = ''
|
||||
set -euo pipefail
|
||||
tailscale cert --min-validity 720h \
|
||||
--cert-file /run/kanidm-tls/certificate.pem \
|
||||
--key-file /run/kanidm-tls/private-key.pem \
|
||||
jeeves.taile39c31.ts.net
|
||||
|
||||
if ! cmp -s /run/kanidm-tls/certificate.pem ${certificateDir}/certificate.pem \
|
||||
|| ! cmp -s /run/kanidm-tls/private-key.pem ${certificateDir}/private-key.pem; then
|
||||
install -m 0640 -o root -g kanidm /run/kanidm-tls/certificate.pem ${certificateDir}/certificate.pem
|
||||
install -m 0640 -o root -g kanidm /run/kanidm-tls/private-key.pem ${certificateDir}/private-key.pem
|
||||
|
||||
# The NixOS service bind-mounts the certificate files, so restart to
|
||||
# pick up renewals. Do not wait on a service that depends on this one.
|
||||
if systemctl is-active --quiet kanidm.service; then
|
||||
systemctl --no-block try-restart kanidm.service
|
||||
fi
|
||||
fi
|
||||
'';
|
||||
};
|
||||
};
|
||||
|
||||
timers.kanidm-tls = {
|
||||
wantedBy = [ "timers.target" ];
|
||||
timerConfig = {
|
||||
OnCalendar = "daily";
|
||||
Persistent = true;
|
||||
RandomizedDelaySec = "1h";
|
||||
};
|
||||
};
|
||||
};
|
||||
}
|
||||
@@ -32,7 +32,7 @@ defaults
|
||||
#Application Setup
|
||||
frontend ContentSwitching
|
||||
bind [::]:80 v4v6
|
||||
bind [::]:443 v4v6 ssl crt /var/lib/acme/audiobookshelf.tmmworkshop.com/full.pem crt /var/lib/acme/cache.tmmworkshop.com/full.pem crt /var/lib/acme/gems.tmmworkshop.com/full.pem crt /var/lib/acme/jellyfin.tmmworkshop.com/full.pem crt /var/lib/acme/share.tmmworkshop.com/full.pem crt /var/lib/acme/gitea.tmmworkshop.com/full.pem
|
||||
bind [::]:443 v4v6 ssl crt /var/lib/acme/audiobookshelf.tmmworkshop.com/full.pem crt /var/lib/acme/auth.tmmworkshop.com/full.pem crt /var/lib/acme/cache.tmmworkshop.com/full.pem crt /var/lib/acme/gems.tmmworkshop.com/full.pem crt /var/lib/acme/jellyfin.tmmworkshop.com/full.pem crt /var/lib/acme/share.tmmworkshop.com/full.pem crt /var/lib/acme/gitea.tmmworkshop.com/full.pem
|
||||
mode http
|
||||
|
||||
# ACME challenge routing (must be first)
|
||||
@@ -40,6 +40,7 @@ frontend ContentSwitching
|
||||
|
||||
# Host ACLs (defined early so rate-limiting can scope to a single vhost)
|
||||
acl host_audiobookshelf hdr(host) -i audiobookshelf.tmmworkshop.com
|
||||
acl host_auth hdr(host) -i auth.tmmworkshop.com
|
||||
acl host_cache hdr(host) -i cache.tmmworkshop.com
|
||||
acl host_jellyfin hdr(host) -i jellyfin.tmmworkshop.com
|
||||
acl host_share hdr(host) -i share.tmmworkshop.com
|
||||
@@ -111,6 +112,7 @@ frontend ContentSwitching
|
||||
|
||||
use_backend acme_challenge if is_acme
|
||||
use_backend audiobookshelf_nodes if host_audiobookshelf
|
||||
use_backend kanidm if host_auth
|
||||
use_backend cache_nodes if host_cache
|
||||
use_backend jellyfin if host_jellyfin
|
||||
use_backend share_nodes if host_share
|
||||
@@ -141,6 +143,15 @@ backend audiobookshelf_nodes
|
||||
mode http
|
||||
server server jeeves:8000
|
||||
|
||||
backend kanidm
|
||||
mode http
|
||||
http-request set-header X-Forwarded-For %[src]
|
||||
http-request set-header X-Forwarded-Proto https
|
||||
option httpchk
|
||||
http-check send meth GET uri /status ver HTTP/1.1 hdr Host auth.tmmworkshop.com
|
||||
http-check expect status 200
|
||||
server kanidm jeeves.taile39c31.ts.net:8443 ssl verify required ca-file /etc/ssl/certs/ca-certificates.crt verifyhost jeeves.taile39c31.ts.net sni str(jeeves.taile39c31.ts.net) check check-sni jeeves.taile39c31.ts.net resolve-prefer ipv4
|
||||
|
||||
backend cache_nodes
|
||||
mode http
|
||||
server server jeeves:5000
|
||||
|
||||
@@ -82,7 +82,6 @@
|
||||
"Michal",
|
||||
"Nornsight",
|
||||
"pydantic",
|
||||
"pyproject",
|
||||
"sandboxing",
|
||||
"syncthing",
|
||||
"vllm",
|
||||
|
||||
Reference in new issue
Block a user