Merge pull request #119126 from fabaff/pycomfoconnect
[NixPkgs.git] / pkgs / servers / home-assistant / default.nix
blob4321fb72c79dce100b5821033f44b581badf03c4
1 { stdenv
2 , lib
3 , fetchFromGitHub
4 , python3
5 , nixosTests
7 # Look up dependencies of specified components in component-packages.nix
8 , extraComponents ? [ ]
10 # Additional packages to add to propagatedBuildInputs
11 , extraPackages ? ps: []
13 # Override Python packages using
14 # self: super: { pkg = super.pkg.overridePythonAttrs (oldAttrs: { ... }); }
15 # Applied after defaultOverrides
16 , packageOverrides ? self: super: {}
18 # Skip pip install of required packages on startup
19 , skipPip ? true }:
21 let
22   defaultOverrides = [
23     # Override the version of some packages pinned in Home Assistant's setup.py
25     # Pinned due to API changes in astral>=2.0, required by the sun/moon plugins
26     # https://github.com/home-assistant/core/issues/36636
27     (mkOverride "astral" "1.10.1"
28       "d2a67243c4503131c856cafb1b1276de52a86e5b8a1d507b7e08bee51cb67bf1")
30     # Pinned due to API changes in iaqualink>=2.0, remove after
31     # https://github.com/home-assistant/core/pull/48137 was merged
32     (self: super: {
33       iaqualink = super.iaqualink.overridePythonAttrs (oldAttrs: rec {
34         version = "0.3.4";
35         src = fetchFromGitHub {
36           owner = "flz";
37           repo = "iaqualink-py";
38           rev = "v${version}";
39           sha256 = "16mn6nd9x3hm6j6da99qhwbqs95hh8wx21r1h1m9csl76z77n9lh";
40         };
41         checkInputs = oldAttrs.checkInputs ++ [ python3.pkgs.asynctest ];
42       });
43     })
45     # Pinned due to API changes in pylilterbot>=2021.3.0
46     (self: super: {
47       pylitterbot = super.pylitterbot.overridePythonAttrs (oldAttrs: rec {
48         version = "2021.2.8";
49         src = fetchFromGitHub {
50           owner = "natekspencer";
51           repo = "pylitterbot";
52           rev = version;
53           sha256 = "142lhijm51v11cd0lhcfdnjdd143jxi2hjsrqdq0rrbbnmj6mymp";
54         };
55         # had no tests before 2021.3.0
56         doCheck = false;
57       });
58     })
60     # Pinned due to bug in ring-doorbell 0.7.0
61     # https://github.com/tchellomello/python-ring-doorbell/issues/240
62     (mkOverride "ring-doorbell" "0.6.2"
63       "fbd537722a27b3b854c26506d894b7399bb8dc57ff36083285971227a2d46560")
65     # Pinned due to API changes in pyruckus>0.12
66     (self: super: {
67       pyruckus = super.pyruckus.overridePythonAttrs (oldAttrs: rec {
68         version = "0.12";
69         src = fetchFromGitHub {
70           owner = "gabe565";
71           repo = "pyruckus";
72           rev = version;
73           sha256 = "0ykv6r6blbj3fg9fplk9i7xclkv5d93rwvx0fm5s8ms9f2s9ih8z";
74         };
75       });
76     })
78     # hass-frontend does not exist in python3.pkgs
79     (self: super: {
80       hass-frontend = self.callPackage ./frontend.nix { };
81     })
82   ];
84   mkOverride = attrname: version: sha256:
85     self: super: {
86       ${attrname} = super.${attrname}.overridePythonAttrs (oldAttrs: {
87         inherit version;
88         src = oldAttrs.src.override {
89           inherit version sha256;
90         };
91       });
92     };
94   py = python3.override {
95     # Put packageOverrides at the start so they are applied after defaultOverrides
96     packageOverrides = lib.foldr lib.composeExtensions (self: super: { }) ([ packageOverrides ] ++ defaultOverrides);
97   };
99   componentPackages = import ./component-packages.nix;
101   availableComponents = builtins.attrNames componentPackages.components;
103   getPackages = component: builtins.getAttr component componentPackages.components;
105   componentBuildInputs = lib.concatMap (component: getPackages component py.pkgs) extraComponents;
107   # Ensure that we are using a consistent package set
108   extraBuildInputs = extraPackages py.pkgs;
110   # Don't forget to run parse-requirements.py after updating
111   hassVersion = "2021.4.4";
113 in with py.pkgs; buildPythonApplication rec {
114   pname = "homeassistant";
115   version = assert (componentPackages.version == hassVersion); hassVersion;
117   # check REQUIRED_PYTHON_VER in homeassistant/const.py
118   disabled = pythonOlder "3.8";
120   # don't try and fail to strip 6600+ python files, it takes minutes!
121   dontStrip = true;
123   inherit availableComponents;
125   # PyPI tarball is missing tests/ directory
126   src = fetchFromGitHub {
127     owner = "home-assistant";
128     repo = "core";
129     rev = version;
130     sha256 = "1p36ymjhirl32ax3xxdg9ia089jw1klww5imq18r6ksajafr78ys";
131   };
133   # leave this in, so users don't have to constantly update their downstream patch handling
134   patches = [
135   ];
137   postPatch = ''
138     substituteInPlace setup.py \
139       --replace "awesomeversion==21.2.3" "awesomeversion" \
140       --replace "bcrypt==3.1.7" "bcrypt" \
141       --replace "cryptography==3.3.2" "cryptography" \
142       --replace "pip>=8.0.3,<20.3" "pip" \
143       --replace "pytz>=2021.1" "pytz" \
144       --replace "pyyaml==5.4.1" "pyyaml" \
145       --replace "ruamel.yaml==0.15.100" "ruamel.yaml"
146     substituteInPlace tests/test_config.py --replace '"/usr"' '"/build/media"'
147   '';
149   propagatedBuildInputs = [
150     # Only packages required in setup.py + hass-frontend
151     aiohttp
152     astral
153     async-timeout
154     attrs
155     awesomeversion
156     bcrypt
157     certifi
158     ciso8601
159     cryptography
160     hass-frontend
161     httpx
162     jinja2
163     pip
164     pyjwt
165     python-slugify
166     pytz
167     pyyaml
168     requests
169     ruamel_yaml
170     voluptuous
171     voluptuous-serialize
172     yarl
173   ] ++ componentBuildInputs ++ extraBuildInputs;
175   makeWrapperArgs = lib.optional skipPip "--add-flags --skip-pip";
177   # upstream only tests on Linux, so do we.
178   doCheck = stdenv.isLinux;
180   checkInputs = [
181     # test infrastructure
182     asynctest
183     pytest-aiohttp
184     pytest-rerunfailures
185     pytest-xdist
186     pytestCheckHook
187     requests-mock
188     # component dependencies
189     pyotp
190     respx
191   ] ++ lib.concatMap (component: getPackages component py.pkgs) componentTests;
193   # We can reasonably test components that don't communicate with any network
194   # services. Before adding new components to this list make sure we have all
195   # its dependencies packaged and listed in ./component-packages.nix.
196   componentTests = [
197     "accuweather"
198     "airly"
199     "analytics"
200     "alert"
201     "api"
202     "auth"
203     "automation"
204     "axis"
205     "bayesian"
206     "binary_sensor"
207     "caldav"
208     "calendar"
209     "camera"
210     "cast"
211     "climate"
212     "cloud"
213     "comfoconnect"
214     "command_line"
215     "config"
216     "configurator"
217     "conversation"
218     "counter"
219     "cover"
220     "deconz"
221     "default_config"
222     "demo"
223     "derivative"
224     "device_automation"
225     "device_sun_light_trigger"
226     "device_tracker"
227     "devolo_home_control"
228     "dhcp"
229     "discovery"
230     "econet"
231     "emulated_hue"
232     "esphome"
233     "fan"
234     "faa_delays"
235     "ffmpeg"
236     "file"
237     "filesize"
238     "filter"
239     "flux"
240     "folder"
241     "folder_watcher"
242     "freebox"
243     "fritzbox"
244     "fritzbox_callmonitor"
245     "frontend"
246     "generic"
247     "generic_thermostat"
248     "geo_json_events"
249     "geo_location"
250     "group"
251     "hddtemp"
252     "history"
253     "history_stats"
254     "home_plus_control"
255     "homekit"
256     "homekit_controller"
257     "homeassistant"
258     "homematic"
259     "homematicip_cloud"
260     "html5"
261     "http"
262     "hue"
263     "iaqualink"
264     "ifttt"
265     "image"
266     "image_processing"
267     "influxdb"
268     "input_boolean"
269     "input_datetime"
270     "input_text"
271     "input_number"
272     "input_select"
273     "intent"
274     "intent_script"
275     "ipp"
276     "kmtronic"
277     "kodi"
278     "light"
279     "litterrobot"
280     "local_file"
281     "local_ip"
282     "lock"
283     "logbook"
284     "logentries"
285     "logger"
286     "lovelace"
287     "lutron_caseta"
288     "manual"
289     "manual_mqtt"
290     "mazda"
291     "media_player"
292     "media_source"
293     "met"
294     "minecraft_server"
295     "mobile_app"
296     "modbus"
297     "moon"
298     "mqtt"
299     "mqtt_eventstream"
300     "mqtt_json"
301     "mqtt_room"
302     "mqtt_statestream"
303     "mullvad"
304     "nexia"
305     "notify"
306     "notion"
307     "number"
308     "omnilogic"
309     "ozw"
310     "panel_custom"
311     "panel_iframe"
312     "persistent_notification"
313     "person"
314     "plaato"
315     "prometheus"
316     "proximity"
317     "push"
318     "python_script"
319     "random"
320     "recorder"
321     "rest"
322     "rest_command"
323     "rituals_perfume_genie"
324     "rmvtransport"
325     "rss_feed_template"
326     "ruckus_unleashed"
327     "safe_mode"
328     "scene"
329     "screenlogic"
330     "script"
331     "search"
332     "shell_command"
333     "shopping_list"
334     "simplisafe"
335     "simulated"
336     "sleepiq"
337     "sma"
338     "sensor"
339     "smarttub"
340     "smtp"
341     "smappee"
342     "solaredge"
343     "sonos"
344     "spotify"
345     "sql"
346     "ssdp"
347     "stream"
348     "subaru"
349     "sun"
350     "switch"
351     "system_health"
352     "system_log"
353     "tag"
354     "tasmota"
355     "tcp"
356     "template"
357     "threshold"
358     "time_date"
359     "timer"
360     "tod"
361     "trace"
362     "tts"
363     "universal"
364     "updater"
365     "upnp"
366     "uptime"
367     "vacuum"
368     "verisure"
369     "weather"
370     "webhook"
371     "websocket_api"
372     "wemo"
373     "wled"
374     "workday"
375     "worldclock"
376     "yeelight"
377     "zeroconf"
378     "zha"
379     "zone"
380     "zwave"
381   ];
383   pytestFlagsArray = [
384     # limit amout of runners to reduce race conditions
385     "-n auto"
386     # retry racy tests that end in "RuntimeError: Event loop is closed"
387     "--reruns 3"
388     "--only-rerun RuntimeError"
389     # assign tests grouped by file to workers
390     "--dist loadfile"
391     # tests are located in tests/
392     "tests"
393     # screenlogic/test_config_flow.py: Tries to send out UDP broadcasts
394     "--deselect tests/components/screenlogic/test_config_flow.py::test_form_cannot_connect"
395     # dynamically add packages required for component tests
396   ] ++ map (component: "tests/components/" + component) componentTests;
398   disabledTestPaths = [
399     # don't bulk test all components
400     "tests/components"
401     # pyotp since v2.4.0 complains about the short mock keys, hass pins v2.3.0
402     "tests/auth/mfa_modules/test_notify.py"
403   ];
405   disabledTests = [
406     # AssertionError: assert 1 == 0
407     "test_error_posted_as_event"
408     "test_merge"
409     # ModuleNotFoundError: No module named 'pyqwikswitch'
410     "test_merge_id_schema"
411     # keyring.errors.NoKeyringError: No recommended backend was available.
412     "test_secrets_from_unrelated_fails"
413     "test_secrets_credstash"
414     # generic/test_camera.py: AssertionError: 500 == 200
415     "test_fetching_without_verify_ssl"
416     "test_fetching_url_with_verify_ssl"
417     # util/test_package.py: AssertionError on package.is_installed('homeassistant>=999.999.999')
418     "test_check_package_version_does_not_match"
419   ];
421   preCheck = ''
422     export HOME="$TEMPDIR"
424     # the tests require the existance of a media dir
425     mkdir /build/media
427     # error out when component test directory is missing, otherwise hidden by xdist execution :(
428     for component in ${lib.concatStringsSep " " (map lib.escapeShellArg componentTests)}; do
429       test -d "tests/components/$component" || {
430         >2& echo "ERROR: Tests for component '$component' were enabled, but they do not exist!"
431         exit 1
432       }
433     done
434   '';
436   passthru = {
437     inherit (py.pkgs) hass-frontend;
438     tests = {
439       inherit (nixosTests) home-assistant;
440     };
441   };
443   meta = with lib; {
444     homepage = "https://home-assistant.io/";
445     description = "Open source home automation that puts local control and privacy first";
446     license = licenses.asl20;
447     maintainers = teams.home-assistant.members;
448     platforms = platforms.linux;
449   };