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