nixpkgs mirror (for testing)
github.com/NixOS/nixpkgs
nix
1{
2 stdenv,
3 lib,
4 callPackage,
5 fetchFromGitHub,
6 fetchPypi,
7 fetchpatch,
8 python313,
9 replaceVars,
10 ffmpeg-headless,
11 ffmpeg_7-headless,
12 inetutils,
13 nixosTests,
14 home-assistant,
15 testers,
16
17 # Look up dependencies of specified components in component-packages.nix
18 extraComponents ? [ ],
19
20 # Additional packages to add to propagatedBuildInputs
21 extraPackages ? ps: [ ],
22
23 # Override Python packages using
24 # self: super: { pkg = super.pkg.overridePythonAttrs (oldAttrs: { ... }); }
25 # Applied after defaultOverrides
26 packageOverrides ? self: super: { },
27
28 # Skip pip install of required packages on startup
29 skipPip ? true,
30}:
31
32let
33 defaultOverrides = [
34 # Override the version of some packages pinned in Home Assistant's setup.py and requirements_all.txt
35
36 (self: super: {
37 aionotion = super.aionotion.overridePythonAttrs rec {
38 version = "2024.03.0";
39 src = fetchFromGitHub {
40 owner = "bachya";
41 repo = "aionotion";
42 tag = version;
43 hash = "sha256-BsbfLb5wCVxR8v2U2Zzt7LMl7XJcZWfVjZN47VDkhFc=";
44 };
45 postPatch = null;
46 };
47
48 aioskybell = super.aioskybell.overridePythonAttrs (oldAttrs: rec {
49 version = "22.7.0";
50 src = fetchFromGitHub {
51 owner = "tkdrob";
52 repo = "aioskybell";
53 tag = version;
54 hash = "sha256-aBT1fDFtq1vasTvCnAXKV2vmZ6LBLZqRCiepv1HDJ+Q=";
55 };
56 });
57
58 aiowatttime = super.aiowatttime.overridePythonAttrs (oldAttrs: rec {
59 version = "0.1.1";
60 src = fetchFromGitHub {
61 owner = "bachya";
62 repo = "aiowatttime";
63 tag = version;
64 hash = "sha256-tWnxGLJT+CRFvkhxFamHxnLXBvoR8tfOvzH1o1i5JJg=";
65 };
66 postPatch = ''
67 substituteInPlace pyproject.toml --replace-fail \
68 '"setuptools >= 35.0.2", "wheel >= 0.29.0", "poetry>=0.12"' \
69 '"poetry-core"'
70 '';
71 });
72
73 astral = super.astral.overridePythonAttrs (oldAttrs: rec {
74 pname = "astral";
75 version = "2.2";
76 src = fetchPypi {
77 inherit pname version;
78 hash = "sha256-5B2ZZ9XEi+QhNGVS8PTe2tQ/85qDV09f8q0ytmJ7b74=";
79 };
80 postPatch = ''
81 substituteInPlace pyproject.toml \
82 --replace-fail "poetry>=1.0.0b1" "poetry-core" \
83 --replace-fail "poetry.masonry" "poetry.core.masonry"
84 '';
85 propagatedBuildInputs = (oldAttrs.propagatedBuildInputs or [ ]) ++ [
86 self.pytz
87 ];
88 });
89
90 imageio = super.imageio.overridePythonAttrs (oldAttrs: {
91 disabledTests = oldAttrs.disabledTests or [ ] ++ [
92 # broken by pyav pin
93 "test_keyframe_intervals"
94 "test_lagging_video_stream"
95 ];
96 });
97
98 google-genai = super.google-genai.overridePythonAttrs rec {
99 version = "1.38.0";
100 src = fetchFromGitHub {
101 owner = "googleapis";
102 repo = "python-genai";
103 tag = "v${version}";
104 hash = "sha256-gJaLEpNKHl6n1MvQDIUW7ynsHYH2eEPGsYso5jSysNg=";
105 };
106 };
107
108 gspread = super.gspread.overridePythonAttrs (oldAttrs: rec {
109 version = "5.12.4";
110 src = fetchFromGitHub {
111 owner = "burnash";
112 repo = "gspread";
113 tag = "v${version}";
114 hash = "sha256-i+QbnF0Y/kUMvt91Wzb8wseO/1rZn9xzeA5BWg1haks=";
115 };
116 dependencies = with self; [
117 requests
118 ];
119 });
120
121 livisi = super.livisi.overridePythonAttrs (oldAttrs: rec {
122 version = "0.0.25";
123 src = fetchFromGitHub {
124 owner = "planbnet";
125 repo = "livisi";
126 tag = "v${version}";
127 hash = "sha256-kEkbuZmYzxhrbTdo7eZJYu2N2uJtfspgqepplXvSXFg=";
128 };
129 });
130
131 notifications-android-tv = super.notifications-android-tv.overridePythonAttrs (oldAttrs: rec {
132 version = "0.1.5";
133 format = "setuptools";
134 pyproject = null;
135
136 src = fetchFromGitHub {
137 owner = "engrbm87";
138 repo = "notifications_android_tv";
139 tag = version;
140 hash = "sha256-adkcUuPl0jdJjkBINCTW4Kmc16C/HzL+jaRZB/Qr09A=";
141 };
142
143 nativeBuildInputs = with self; [
144 setuptools
145 ];
146
147 propagatedBuildInputs = with self; [
148 requests
149 ];
150
151 doCheck = false; # no tests
152 });
153
154 openhomedevice = super.openhomedevice.overridePythonAttrs (oldAttrs: rec {
155 version = "2.2";
156 src = fetchFromGitHub {
157 inherit (oldAttrs.src) owner repo;
158 tag = version;
159 hash = "sha256-GGp7nKFH01m1KW6yMkKlAdd26bDi8JDWva6OQ0CWMIw=";
160 };
161 });
162
163 plexapi = super.plexapi.overrideAttrs (oldAttrs: rec {
164 version = "4.15.16";
165 src = fetchFromGitHub {
166 owner = "pkkid";
167 repo = "python-plexapi";
168 tag = version;
169 hash = "sha256-NwGGNN6LC3gvE8zoVL5meNWMbqZjJ+6PcU2ebJTfJmU=";
170 };
171 });
172
173 # Pinned due to API changes in 0.1.0
174 poolsense = super.poolsense.overridePythonAttrs (oldAttrs: rec {
175 version = "0.0.8";
176 src = fetchPypi {
177 pname = "poolsense";
178 inherit version;
179 hash = "sha256-17MHrYRmqkH+1QLtgq2d6zaRtqvb9ju9dvPt9gB2xCc=";
180 };
181 });
182
183 py-madvr2 = super.py-madvr2.overridePythonAttrs (oldAttrs: rec {
184 version = "1.6.40";
185 src = fetchFromGitHub {
186 owner = "iloveicedgreentea";
187 repo = "py-madvr";
188 tag = "v${version}";
189 hash = "sha256-0IX57Sa/oXGiViD39FVBRa2jxuKuZ3UNsOTHwuBdmWs=";
190 };
191 pythonImportsCheck = [ "madvr" ];
192 disabledTests = oldAttrs.disabledTests ++ [
193 "test_async_add_tasks"
194 "test_send_heartbeat"
195 ];
196 });
197
198 # Pinned due to API changes >0.3.5.3
199 pyatag = super.pyatag.overridePythonAttrs (oldAttrs: rec {
200 version = "0.3.5.3";
201 src = fetchFromGitHub {
202 owner = "MatsNl";
203 repo = "pyatag";
204 rev = version;
205 sha256 = "00ly4injmgrj34p0lyx7cz2crgnfcijmzc0540gf7hpwha0marf6";
206 };
207 });
208
209 pydexcom = super.pydexcom.overridePythonAttrs (oldAttrs: rec {
210 version = "0.2.3";
211 src = fetchFromGitHub {
212 owner = "gagebenne";
213 repo = "pydexcom";
214 tag = version;
215 hash = "sha256-ItDGnUUUTwCz4ZJtFVlMYjjoBPn2h8QZgLzgnV2T/Qk=";
216 };
217 });
218
219 pyflume = super.pyflume.overridePythonAttrs (oldAttrs: rec {
220 version = "0.6.5";
221 src = fetchFromGitHub {
222 owner = "ChrisMandich";
223 repo = "PyFlume";
224 tag = "v${version}";
225 hash = "sha256-kIE3y/qlsO9Y1MjEQcX0pfaBeIzCCHk4f1Xa215BBHo=";
226 };
227 dependencies = oldAttrs.propagatedBuildInputs or [ ] ++ [
228 self.pytz
229 ];
230 });
231
232 pysnooz = super.pysnooz.overridePythonAttrs (oldAttrs: rec {
233 version = "0.8.6";
234 src = fetchFromGitHub {
235 owner = "AustinBrunkhorst";
236 repo = "pysnooz";
237 tag = "v${version}";
238 hash = "sha256-hJwIObiuFEAVhgZXYB9VCeAlewBBnk0oMkP83MUCpyU=";
239 };
240 patches = [ ];
241 doCheck = false;
242 });
243
244 pytradfri = super.pytradfri.overridePythonAttrs (oldAttrs: rec {
245 version = "9.0.1";
246 src = fetchFromGitHub {
247 owner = "home-assistant-libs";
248 repo = "pytradfri";
249 tag = version;
250 hash = "sha256-xOdTzG0bF5p1QpkXv2btwrVugQRjSwdAj8bXcC0IoQg=";
251 };
252 patches = [ ];
253 doCheck = false;
254 });
255
256 wolf-comm = super.wolf-comm.overridePythonAttrs rec {
257 version = "0.0.23";
258 src = fetchFromGitHub {
259 owner = "janrothkegel";
260 repo = "wolf-comm";
261 tag = version;
262 hash = "sha256-LpehooW3vmohiyMwOQTFNLiNCsaLKelWQxQk8bl+y1k=";
263 };
264 };
265
266 # internal python packages only consumed by home-assistant itself
267 hass-web-proxy-lib = self.callPackage ./python-modules/hass-web-proxy-lib { };
268 home-assistant-frontend = self.callPackage ./frontend.nix { };
269 home-assistant-intents = self.callPackage ./intents.nix { };
270 homeassistant = self.toPythonModule home-assistant;
271 pytest-homeassistant-custom-component =
272 self.callPackage ./pytest-homeassistant-custom-component.nix
273 { };
274 })
275 ];
276
277 python = python313.override {
278 self = python;
279 packageOverrides = lib.composeManyExtensions (defaultOverrides ++ [ packageOverrides ]);
280 };
281
282 componentPackages = import ./component-packages.nix;
283
284 availableComponents = builtins.attrNames componentPackages.components;
285
286 inherit (componentPackages) supportedComponentsWithTests;
287
288 getPackages = component: componentPackages.components.${component};
289
290 componentBuildInputs = lib.concatMap (component: getPackages component python.pkgs) extraComponents;
291
292 # Ensure that we are using a consistent package set
293 extraBuildInputs = extraPackages python.pkgs;
294
295 # Don't forget to run update-component-packages.py after updating
296 hassVersion = "2026.1.3";
297
298in
299python.pkgs.buildPythonApplication rec {
300 pname = "homeassistant";
301 version =
302 assert (componentPackages.version == hassVersion);
303 hassVersion;
304 pyproject = true;
305
306 # check REQUIRED_PYTHON_VER in homeassistant/const.py
307 disabled = python.pythonOlder "3.13";
308
309 # don't try and fail to strip 6600+ python files, it takes minutes!
310 dontStrip = true;
311
312 # Primary source is the git, which has the tests and allows bisecting the core
313 src = fetchFromGitHub {
314 owner = "home-assistant";
315 repo = "core";
316 tag = version;
317 hash = "sha256-zmS5OZaUFe45rbCil7sbVlhy0wwA+F9tBO10KvBM2PY=";
318 };
319
320 # Secondary source is pypi sdist for translations
321 sdist = fetchPypi {
322 inherit pname version;
323 hash = "sha256-gs5YyR1MofSMV8TDeBGp9keIREcszZGcLvtnHOYR7uc=";
324 };
325
326 build-system = with python.pkgs; [
327 setuptools
328 ];
329
330 pythonRelaxDeps = true;
331
332 # extract translations from pypi sdist
333 prePatch = ''
334 tar --extract --gzip --file $sdist --strip-components 1 --wildcards "**/translations"
335 '';
336
337 # leave this in, so users don't have to constantly update their downstream patch handling
338 patches = [
339 # Follow symlinks in /var/lib/hass/www
340 ./patches/static-follow-symlinks.patch
341
342 # Copy default blueprints without preserving permissions
343 ./patches/default-blueprint-permissions.patch
344
345 # Patch path to ffmpeg binary
346 (replaceVars ./patches/ffmpeg-path.patch {
347 ffmpeg = "${lib.getExe ffmpeg-headless}";
348 })
349
350 (fetchpatch {
351 # pytest 9 renames some snapshots
352 name = "revert-to-pytest-8.patch";
353 url = "https://github.com/home-assistant/core/commit/3f22dbaa2e1a7776185ec443bf26f90e90e55efa.patch";
354 revert = true;
355 hash = "sha256-rHXpmHUNCr+lhYqiOVrCSQTWvWJ+jHNwPJzUeFtDPIw=";
356 })
357 ];
358
359 postPatch = ''
360 substituteInPlace tests/test_core_config.py --replace-fail '"/usr"' "\"$NIX_BUILD_TOP/media\""
361
362 substituteInPlace pyproject.toml \
363 --replace-fail "setuptools==78.1.1" setuptools
364 '';
365
366 pythonRemoveDeps = [
367 "uv"
368 ];
369
370 dependencies = with python.pkgs; [
371 # Only packages required in pyproject.toml
372 aiodns
373 aiohasupervisor
374 aiohttp
375 aiohttp-asyncmdnsresolver
376 aiohttp-cors
377 aiohttp-fast-zlib
378 aiozoneinfo
379 annotatedyaml
380 astral
381 async-interrupt
382 atomicwrites-homeassistant
383 attrs
384 audioop-lts
385 awesomeversion
386 bcrypt
387 certifi
388 ciso8601
389 cronsim
390 cryptography
391 fnv-hash-fast
392 hass-nabucasa
393 home-assistant-bluetooth
394 httpx
395 ifaddr
396 jinja2
397 lru-dict
398 orjson
399 packaging
400 pillow
401 propcache
402 psutil-home-assistant
403 pyjwt
404 pyopenssl
405 python-slugify
406 pyyaml
407 requests
408 securetar
409 sqlalchemy
410 standard-aifc
411 standard-telnetlib
412 typing-extensions
413 ulid-transform
414 urllib3
415 voluptuous
416 voluptuous-openapi
417 voluptuous-serialize
418 webrtc-models
419 yarl
420 zeroconf
421 # REQUIREMENTS in homeassistant/auth/mfa_modules/totp.py and homeassistant/auth/mfa_modules/notify.py
422 pyotp
423 pyqrcode
424 ];
425
426 makeWrapperArgs = lib.optional skipPip "--add-flags --skip-pip";
427
428 # upstream only tests on Linux, so do we.
429 doCheck = stdenv.hostPlatform.isLinux;
430
431 requirementsTest = with python.pkgs; [
432 # test infrastructure (selectively from requirement_test.txt)
433 freezegun
434 pytest-asyncio
435 pytest-aiohttp
436 pytest-freezer
437 pytest-socket
438 pytest-timeout
439 pytest-unordered
440 pytest-xdist
441 pytestCheckHook
442 requests-mock
443 respx
444 syrupy
445 ];
446
447 nativeCheckInputs =
448 requirementsTest
449 ++ (with python.pkgs; [
450 # Used in tests/non_packaged_scripts/test_alexa_locales.py
451 beautifulsoup4
452 # Used in tests/scripts/test_check_config.py
453 colorlog
454 ])
455 ++ lib.concatMap (component: getPackages component python.pkgs) [
456 # some components are needed even if tests in tests/components are disabled
457 "assist_pipeline"
458 "frontend"
459 "hue"
460 "mobile_app"
461 ];
462
463 pytestFlags = [
464 # assign tests grouped by file to workers
465 "--dist=loadfile"
466 # enable full variable printing on error
467 "--showlocals"
468 ];
469
470 enabledTestPaths = [
471 # tests are located in tests/
472 "tests"
473 ];
474
475 disabledTestPaths = [
476 # we neither run nor distribute hassfest
477 "tests/hassfest"
478 # we don't care about code quality
479 "tests/pylint"
480 # redundant component import test, which would make debugpy & sentry expensive to review
481 "tests/test_circular_imports.py"
482 # don't bulk test all components
483 "tests/components"
484 # AssertionError: assert 1 == 0
485 "tests/test_config.py::test_merge"
486 # checks whether pip is installed
487 "tests/util/test_package.py::test_check_package_fragment"
488 # flaky
489 "tests/test_bootstrap.py::test_setup_hass_takes_longer_than_log_slow_startup"
490 "tests/test_test_fixtures.py::test_evict_faked_translations"
491 "tests/helpers/test_backup.py::test_async_get_manager"
492 "tests/helpers/test_trigger.py::test_platform_multiple_triggers[sync_action]"
493 ];
494
495 preCheck = ''
496 export HOME="$TEMPDIR"
497 export PYTHONASYNCIODEBUG=1
498
499 # the tests require the existance of a media dir
500 mkdir "$NIX_BUILD_TOP"/media
501
502 # put ping binary into PATH, e.g. for wake_on_lan tests
503 export PATH=${inetutils}/bin:$PATH
504 '';
505
506 passthru = {
507 inherit
508 availableComponents
509 extraComponents
510 getPackages
511 python
512 supportedComponentsWithTests
513 ;
514 pythonPath = python.pkgs.makePythonPath (componentBuildInputs ++ extraBuildInputs);
515 frontend = python.pkgs.home-assistant-frontend;
516 intents = python.pkgs.home-assistant-intents;
517 tests = {
518 nixos = nixosTests.home-assistant;
519 components = callPackage ./tests.nix { };
520 version = testers.testVersion {
521 package = home-assistant;
522 command = "hass --version";
523 };
524 withoutCheckDeps = home-assistant.overridePythonAttrs {
525 pname = "home-assistant-without-check-deps";
526 doCheck = false;
527 };
528 };
529 };
530
531 meta = {
532 homepage = "https://home-assistant.io/";
533 changelog = "https://github.com/home-assistant/core/releases/tag/${src.tag}";
534 description = "Open source home automation that puts local control and privacy first";
535 license = lib.licenses.asl20;
536 teams = [ lib.teams.home-assistant ];
537 platforms = lib.platforms.linux;
538 mainProgram = "hass";
539 };
540}