me like nix
1{
2 pkgs,
3 config,
4 ...
5}:
6
7{
8 imports = [
9 # Include the results of the hardware scan.
10 ./hardware-configuration.nix
11 ../common/common.nix
12 ];
13
14 networking.hostName = "mira"; # Define your hostname.
15
16 # ZRAM swap to prevent OOM freezes
17 zramSwap = {
18 enable = true;
19 memoryPercent = 50;
20 };
21 # networking.wireless.enable = true; # Enables wireless support via wpa_supplicant.
22
23 # Configure network proxy if necessary
24 # networking.proxy.default = "http://user:password@proxy:port/";
25 # networking.proxy.noProxy = "127.0.0.1,localhost,internal.domain";
26
27 # this is like a network devices discovery thing
28 services.avahi = {
29 enable = true;
30 nssmdns4 = true;
31 openFirewall = true;
32 };
33
34 services.copyparty.enable = true;
35
36 services.openssh = {
37 enable = true;
38 settings = {
39 PasswordAuthentication = false;
40 KbdInteractiveAuthentication = false;
41 PermitRootLogin = "no";
42 AllowUsers = [ "sean" ];
43 };
44 };
45
46 users.users.sean.openssh.authorizedKeys.keys = [
47 "ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABgQDCIqgZ7kedxo+mOW7YG73Vp3zel3h180y3GKvHtRsXfGlpIIvRDy7pgCBQ4AGXYD4y78URQmFohYSAPqCPOPaWcU2un3XG9KvCzEsHmsbskPonitUmCiKvrKkb6oW4jCBtd7AEtBn+AiajAQFtPZ7NN2Df3AmTypvR6Irg7R+nxnfc9NTIHmGvxSDyWcbb4pguL20sctUSqGL6xGh8q/bqhdOThSimM+z9bEUNxK/5rPhwkNniMrp4pJcUrUiAh5/4DiRFG6KT+oeg+/myoz/Z1sPvAs7u/8JDQI4RshRD8Hu0oTkRBN6Hxj478q2SXbeBUZlD6IdjP3RhGpmSecoDdtWqKbpuV3eVRtQtba3KL86GBeV/bugaOdJ1Aud+1SOFJreAAuvxzMMKT+cdQZk6oOPP148DA/No+mDm/2S43lcdCXh79wA6YRAmKQ8jmZxTCtPutrvuZK1rguvvUlEoG/vhdNHh7eDa4Td07V6bjCRPUl8qk/e4M0E3pwsTlZc="
48 ];
49
50 # List services that you want to enable:
51 services.flaresolverr.enable = true;
52 nixarr = {
53 enable = true;
54 mediaDir = "/mnt/storage1/nixarr/media";
55 vpn = {
56 enable = true;
57 wgConf = "/mnt/storage1/nixarr/wireguard.conf";
58 };
59
60 jellyfin = {
61 enable = true;
62 openFirewall = true;
63 };
64
65 transmission = {
66 enable = true;
67 vpn.enable = true;
68 };
69 sabnzbd = {
70 enable = true;
71 vpn.enable = true;
72 openFirewall = true;
73 };
74
75 prowlarr.enable = true;
76 radarr.enable = true;
77 sonarr.enable = true;
78 jellyseerr = {
79 enable = true;
80 openFirewall = true;
81 };
82
83 recyclarr = {
84 enable = true;
85 configuration = {
86 sonarr = {
87 series = {
88 base_url = "http://localhost:8989";
89 api_key = "!env_var SONARR_API_KEY";
90 quality_definition = {
91 type = "series";
92 };
93 delete_old_custom_formats = true;
94 custom_formats = [
95 {
96 trash_ids = [
97 "85c61753df5da1fb2aab6f2a47426b09" # BR-DISK
98 "9c11cd3f07101cdba90a2d81cf0e56b4" # LQ
99 ];
100 assign_scores_to = [
101 {
102 name = "WEB-DL (1080p)";
103 score = -10000;
104 }
105 ];
106 }
107 ];
108 };
109 };
110 radarr = {
111 movies = {
112 base_url = "http://localhost:7878";
113 api_key = "!env_var RADARR_API_KEY";
114 quality_definition = {
115 type = "movie";
116 };
117 delete_old_custom_formats = true;
118 custom_formats = [
119 {
120 trash_ids = [
121 "570bc9ebecd92723d2d21500f4be314c" # Remaster
122 "eca37840c13c6ef2dd0262b141a5482f" # 4K Remaster
123 ];
124 assign_scores_to = [
125 {
126 name = "HD Bluray + WEB";
127 score = 25;
128 }
129 ];
130 }
131 ];
132 };
133 };
134 };
135 };
136 };
137
138 # MQTT broker for Home Assistant (Tasmota devices, Frigate)
139 services.mosquitto = {
140 enable = true;
141 listeners = [
142 {
143 acl = [ "pattern readwrite #" ];
144 omitPasswordAuth = true;
145 settings.allow_anonymous = true;
146 }
147 ];
148 };
149
150 # Frigate NVR for camera recording and AI object detection
151 services.frigate = {
152 enable = true;
153 hostname = "frigate";
154 settings = {
155 mqtt = {
156 enabled = true;
157 host = "localhost";
158 port = 1883;
159 };
160
161 # Detector configuration - CPU for now (can add GPU later)
162 detectors = {
163 cpu = {
164 type = "cpu";
165 num_threads = 4;
166 };
167 };
168
169 # Camera configuration - Pi Camera via go2rtc
170 cameras = {
171 picam = {
172 enabled = true;
173 ffmpeg = {
174 inputs = [
175 {
176 path = "rtsp://pi:8554/picam";
177 roles = [ "detect" "record" ];
178 }
179 ];
180 };
181 detect = {
182 enabled = true;
183 width = 1920;
184 height = 1080;
185 fps = 5; # Lower FPS for detection to reduce CPU
186 };
187 record = {
188 enabled = true;
189 retain = {
190 days = 7;
191 mode = "active_objects";
192 };
193 };
194 snapshots = {
195 enabled = true;
196 bounding_box = true;
197 retain = {
198 default = 14;
199 };
200 };
201 objects = {
202 track = [ "person" "dog" "cat" "car" ];
203 };
204 };
205
206 pizerocam = {
207 enabled = true;
208 ffmpeg = {
209 inputs = [
210 {
211 path = "rtsp://pizero:8554/pizerocam";
212 roles = [ "detect" "record" ];
213 }
214 ];
215 };
216 detect = {
217 enabled = true;
218 width = 1280;
219 height = 720;
220 fps = 5;
221 };
222 record = {
223 enabled = true;
224 retain = {
225 days = 7;
226 mode = "active_objects";
227 };
228 };
229 snapshots = {
230 enabled = true;
231 bounding_box = true;
232 retain = {
233 default = 14;
234 };
235 };
236 objects = {
237 track = [ "person" "dog" "cat" "car" ];
238 };
239 };
240 };
241
242 # Recording storage
243 record = {
244 enabled = true;
245 retain = {
246 days = 7;
247 mode = "active_objects";
248 };
249 };
250 };
251 };
252
253 # Home Assistant service
254 services.home-assistant = {
255 enable = true;
256 customComponents = with pkgs.home-assistant-custom-components; [
257 frigate
258 ];
259 extraComponents = [
260 "esphome"
261 "met"
262 "radio_browser"
263 "homekit"
264 "homekit_controller"
265 "isal"
266 "mqtt"
267 "tasmota"
268 "wiz"
269 "google_translate" # TTS - was missing gtts module
270 "ecobee" # Was missing pyecobee module
271 "ibeacon" # Was missing ibeacon_ble module
272 "go2rtc" # Camera streaming
273 "generic" # Generic camera integration
274 ];
275 config = {
276 homeassistant = {
277 time_zone = "America/Toronto";
278 };
279 default_config = { };
280 zeroconf = { };
281 # MQTT configuration - broker must be set up via UI
282 mqtt = { };
283 # Automations
284 automation = [
285 {
286 id = "1761448856909";
287 alias = "Lower heat at night";
288 trigger = [
289 {
290 platform = "time";
291 at = "23:00:00";
292 }
293 ];
294 condition = [ ];
295 action = [
296 {
297 action = "climate.set_temperature";
298 target.device_id = "bfe22d32a4532f8ae991d6daffb48267";
299 data = {
300 hvac_mode = "heat";
301 temperature = 18;
302 };
303 }
304 ];
305 mode = "single";
306 }
307 {
308 id = "1766200000001";
309 alias = "Raise heat in morning";
310 trigger = [
311 {
312 platform = "time";
313 at = "06:00:00";
314 }
315 ];
316 condition = [ ];
317 action = [
318 {
319 action = "climate.set_temperature";
320 target.device_id = "bfe22d32a4532f8ae991d6daffb48267";
321 data = {
322 hvac_mode = "heat";
323 temperature = 21;
324 };
325 }
326 ];
327 mode = "single";
328 }
329 {
330 id = "1766153071796";
331 alias = "Close Garage Door";
332 trigger = [
333 {
334 platform = "device";
335 device_id = "d8dedd8cd0ce1488d9830c455bb0a761";
336 domain = "cover";
337 entity_id = "cf36763543169888aa106b1acb02ad72";
338 type = "opened";
339 for = {
340 hours = 0;
341 minutes = 10;
342 seconds = 0;
343 };
344 }
345 ];
346 condition = [ ];
347 action = [
348 {
349 device_id = "d8dedd8cd0ce1488d9830c455bb0a761";
350 domain = "cover";
351 entity_id = "cf36763543169888aa106b1acb02ad72";
352 type = "close";
353 }
354 ];
355 mode = "single";
356 }
357 ];
358 };
359 };
360
361 # Enable the OpenSSH daemon.
362 # services.openssh.enable = true;
363
364 security.pam.loginLimits = [
365 {
366 domain = "*";
367 type = "soft";
368 item = "nofile";
369 value = "8192";
370 }
371 ];
372
373 # Open ports in the firewall.
374 networking.firewall.allowedTCPPorts = [
375 8096 # jellyfin
376 5055 # jellyseer
377 3000 # vite dev port
378 1883 # MQTT for Tasmota devices
379 2300 # trmnl
380 5000 # Frigate web UI
381 8971 # Frigate API
382 config.services.home-assistant.config.http.server_port
383 ];
384 networking.firewall.allowedUDPPorts = [
385 ];
386 # networking.firewall.enable = false;
387
388 # This value determines the NixOS release from which the default
389 # settings for stateful data, like file locations and database versions
390 # on your system were taken. It‘s perfectly fine and recommended to leave
391 # this value at the release version of the first install of this system.
392 # Before changing this value read the documentation for this option
393 # (e.g. man configuration.nix or on https://nixos.org/nixos/options.html).
394 system.stateVersion = "25.05"; # Did you read the comment?
395
396}