diff --git a/nixos/modules/services/misc/home-assistant.nix b/nixos/modules/services/misc/home-assistant.nix index bc463d3e670c..4fbf5a412d12 100644 --- a/nixos/modules/services/misc/home-assistant.nix +++ b/nixos/modules/services/misc/home-assistant.nix @@ -6,6 +6,19 @@ let cfg = config.services.home-assistant; configFile = pkgs.writeText "configuration.yaml" (builtins.toJSON cfg.config); + + availableComponents = pkgs.home-assistant.availableComponents; + + # Returns whether component is used in config + useComponent = component: hasAttrByPath (splitString "." component) cfg.config; + + # List of components used in config + extraComponents = filter useComponent availableComponents; + + package = if cfg.autoExtraComponents + then (cfg.package.override { inherit extraComponents; }) + else cfg.package; + in { meta.maintainers = with maintainers; [ dotlambda ]; @@ -29,6 +42,7 @@ in { }; frontend = { }; http = { }; + feedreader.urls = [ "https://nixos.org/blogs.xml" ]; } ''; description = '' @@ -48,10 +62,22 @@ in { ''; description = '' Home Assistant package to use. - Most Home Assistant components require additional dependencies, - which are best specified by overriding pkgs.home-assistant. - You can find the dependencies by searching for failed imports in your log or by looking at this list: - + Override extraPackages in order to add additional dependencies. + ''; + }; + + autoExtraComponents = mkOption { + default = true; + type = types.bool; + description = '' + If set to true, the components used in config + are set as the specified package's extraComponents. + This in turn adds all packaged dependencies to the derivation. + You might still see import errors in your log. + In this case, you will need to package the necessary dependencies yourself + or ask for someone else to package them. + If a dependency is packaged but not automatically added to this list, + you might need to specify it in extraPackages. ''; }; }; @@ -67,7 +93,7 @@ in { ''; serviceConfig = { ExecStart = '' - ${cfg.package}/bin/hass --config "${cfg.configDir}" + ${package}/bin/hass --config "${cfg.configDir}" ''; User = "hass"; Group = "hass"; diff --git a/nixos/tests/home-assistant.nix b/nixos/tests/home-assistant.nix index 0e2fee8e808d..5d7e0ec65e73 100644 --- a/nixos/tests/home-assistant.nix +++ b/nixos/tests/home-assistant.nix @@ -17,13 +17,16 @@ in { homeassistant = { name = "Home"; time_zone = "UTC"; + latitude = "0.0"; + longitude = "0.0"; + elevation = 0; }; frontend = { }; http = { }; }; }; }; - }; + }; testScript = '' startAll; @@ -37,5 +40,7 @@ in { $hass->waitForOpenPort(8123); $hass->succeed("curl --fail http://localhost:8123/states"); $hass->succeed("curl --fail http://localhost:8123/api/ | grep 'API running'"); + + $hass->fail("cat ${configDir}/home-assistant.log | grep -qF ERROR"); ''; }) diff --git a/pkgs/servers/home-assistant/component-packages.nix b/pkgs/servers/home-assistant/component-packages.nix new file mode 100644 index 000000000000..679ca2afd43a --- /dev/null +++ b/pkgs/servers/home-assistant/component-packages.nix @@ -0,0 +1,431 @@ +# Generated from parse-requirements.py +# Do not edit! + +{ + version = "0.62.1"; + components = { + "nuimo_controller" = ps: with ps; [ ]; + "bbb_gpio" = ps: with ps; [ ]; + "doorbird" = ps: with ps; [ ]; + "isy994" = ps: with ps; [ ]; + "notify.html5" = ps: with ps; [ pyjwt ]; + "sensor.mvglive" = ps: with ps; [ ]; + "arduino" = ps: with ps; [ ]; + "xiaomi_aqara" = ps: with ps; [ ]; + "rpi_gpio" = ps: with ps; [ ]; + "remember_the_milk" = ps: with ps; [ httplib2 ]; + "media_player.sonos" = ps: with ps; [ ]; + "sensor.travisci" = ps: with ps; [ ]; + "notify.twitter" = ps: with ps; [ ]; + "notify.yessssms" = ps: with ps; [ ]; + "abode" = ps: with ps; [ ]; + "device_tracker.automatic" = ps: with ps; [ ]; + "sensor.dnsip" = ps: with ps; [ aiodns ]; + "emulated_hue" = ps: with ps; [ aiohttp-cors ]; + "http" = ps: with ps; [ aiohttp-cors ]; + "sensor.imap" = ps: with ps; [ ]; + "light.lifx" = ps: with ps; [ ]; + "scene.hunterdouglas_powerview" = ps: with ps; [ ]; + "alarmdecoder" = ps: with ps; [ ]; + "sensor.alpha_vantage" = ps: with ps; [ ]; + "amcrest" = ps: with ps; [ ]; + "media_player.anthemav" = ps: with ps; [ ]; + "apcupsd" = ps: with ps; [ ]; + "notify.apns" = ps: with ps; [ ]; + "asterisk_mbox" = ps: with ps; [ ]; + "light.avion" = ps: with ps; [ ]; + "axis" = ps: with ps; [ ]; + "tts.baidu" = ps: with ps; [ ]; + "sensor.modem_callerid" = ps: with ps; [ ]; + "sensor.linux_battery" = ps: with ps; [ batinfo ]; + "sensor.eddystone_temperature" = ps: with ps; [ ]; + "device_tracker.linksys_ap" = ps: with ps; [ beautifulsoup4 ]; + "sensor.geizhals" = ps: with ps; [ beautifulsoup4 ]; + "sensor.scrape" = ps: with ps; [ beautifulsoup4 ]; + "sensor.sytadin" = ps: with ps; [ beautifulsoup4 ]; + "zha" = ps: with ps; [ ]; + "blink" = ps: with ps; [ ]; + "light.blinksticklight" = ps: with ps; [ BlinkStick ]; + "light.blinkt" = ps: with ps; [ ]; + "sensor.bitcoin" = ps: with ps; [ ]; + "light.decora" = ps: with ps; [ ]; + "sensor.bme680" = ps: with ps; [ ]; + "notify.aws_lambda" = ps: with ps; [ boto3 ]; + "notify.aws_sns" = ps: with ps; [ boto3 ]; + "notify.aws_sqs" = ps: with ps; [ boto3 ]; + "tts.amazon_polly" = ps: with ps; [ boto3 ]; + "sensor.broadlink" = ps: with ps; [ ]; + "switch.broadlink" = ps: with ps; [ ]; + "sensor.buienradar" = ps: with ps; [ ]; + "weather.buienradar" = ps: with ps; [ ]; + "calendar.caldav" = ps: with ps; [ ]; + "notify.ciscospark" = ps: with ps; [ ]; + "coinbase" = ps: with ps; [ ]; + "sensor.coinmarketcap" = ps: with ps; [ ]; + "alarm_control_panel.concord232" = ps: with ps; [ ]; + "binary_sensor.concord232" = ps: with ps; [ ]; + "sensor.crimereports" = ps: with ps; [ ]; + "datadog" = ps: with ps; [ datadog ]; + "sensor.metoffice" = ps: with ps; [ ]; + "weather.metoffice" = ps: with ps; [ ]; + "light.decora_wifi" = ps: with ps; [ ]; + "device_tracker.upc_connect" = ps: with ps; [ defusedxml ]; + "sensor.deluge" = ps: with ps; [ ]; + "switch.deluge" = ps: with ps; [ ]; + "media_player.denonavr" = ps: with ps; [ ]; + "media_player.directv" = ps: with ps; [ ]; + "sensor.discogs" = ps: with ps; [ discogs_client ]; + "notify.discord" = ps: with ps; [ ]; + "updater" = ps: with ps; [ distro ]; + "switch.digitalloggers" = ps: with ps; [ ]; + "notify.xmpp" = ps: with ps; [ pyasn1-modules pyasn1 sleekxmpp ]; + "sensor.dovado" = ps: with ps; [ ]; + "sensor.dsmr" = ps: with ps; [ ]; + "dweet" = ps: with ps; [ ]; + "sensor.dweet" = ps: with ps; [ ]; + "sensor.eliqonline" = ps: with ps; [ ]; + "enocean" = ps: with ps; [ ]; + "sensor.envirophat" = ps: with ps; [ ]; + "sensor.season" = ps: with ps; [ ephem ]; + "keyboard_remote" = ps: with ps; [ ]; + "climate.honeywell" = ps: with ps; [ ]; + "image_processing.dlib_face_detect" = ps: with ps; [ ]; + "image_processing.dlib_face_identify" = ps: with ps; [ ]; + "sensor.fastdotcom" = ps: with ps; [ ]; + "sensor.fedex" = ps: with ps; [ ]; + "feedreader" = ps: with ps; [ feedparser ]; + "sensor.geo_rss_events" = ps: with ps; [ feedparser ]; + "sensor.fitbit" = ps: with ps; [ ]; + "sensor.fixer" = ps: with ps; [ ]; + "light.flux_led" = ps: with ps; [ ]; + "notify.free_mobile" = ps: with ps; [ ]; + "device_tracker.fritz" = ps: with ps; [ ]; + "sensor.fritzbox_callmonitor" = ps: with ps; [ ]; + "sensor.fritzbox_netmonitor" = ps: with ps; [ ]; + "switch.fritzdect" = ps: with ps; [ ]; + "media_player.frontier_silicon" = ps: with ps; [ ]; + "conversation" = ps: with ps; [ ]; + "tts.google" = ps: with ps; [ ]; + "device_tracker.bluetooth_le_tracker" = ps: with ps; [ ]; + "sensor.gearbest" = ps: with ps; [ ]; + "sensor.gitter" = ps: with ps; [ ]; + "notify.gntp" = ps: with ps; [ ]; + "google" = ps: with ps; [ google_api_python_client oauth2client ]; + "sensor.google_travel_time" = ps: with ps; [ ]; + "sensor.gpsd" = ps: with ps; [ ]; + "light.greenwave" = ps: with ps; [ ]; + "media_player.gstreamer" = ps: with ps; [ ]; + "ffmpeg" = ps: with ps; [ ]; + "media_player.philips_js" = ps: with ps; [ ]; + "mqtt.server" = ps: with ps; [ hbmqtt ]; + "climate.heatmiser" = ps: with ps; [ ]; + "switch.hikvisioncam" = ps: with ps; [ ]; + "notify.hipchat" = ps: with ps; [ ]; + "binary_sensor.workday" = ps: with ps; [ ]; + "frontend" = ps: with ps; [ user-agents ]; + "camera.onvif" = ps: with ps; [ ]; + "sensor.dht" = ps: with ps; [ ]; + "media_player.braviatv" = ps: with ps; [ ]; + "media_player.spotify" = ps: with ps; [ ]; + "netatmo" = ps: with ps; [ ]; + "neato" = ps: with ps; [ ]; + "sensor.sabnzbd" = ps: with ps; [ ]; + "switch.anel_pwrctrl" = ps: with ps; [ ]; + "switch.edimax" = ps: with ps; [ ]; + "sensor.gtfs" = ps: with ps; [ ]; + "binary_sensor.flic" = ps: with ps; [ ]; + "media_player.lg_netcast" = ps: with ps; [ ]; + "sensor.bh1750" = ps: with ps; [ ]; + "sensor.bme280" = ps: with ps; [ ]; + "sensor.htu21d" = ps: with ps; [ ]; + "light.iglo" = ps: with ps; [ ]; + "ihc" = ps: with ps; [ ]; + "influxdb" = ps: with ps; [ influxdb ]; + "sensor.influxdb" = ps: with ps; [ influxdb ]; + "insteon_local" = ps: with ps; [ ]; + "insteon_plm" = ps: with ps; [ ]; + "verisure" = ps: with ps; [ ]; + "media_player.kodi" = ps: with ps; [ ]; + "notify.kodi" = ps: with ps; [ ]; + "device_tracker.owntracks" = ps: with ps; [ libnacl ]; + "device_tracker.owntracks_http" = ps: with ps; [ libnacl ]; + "dyson" = ps: with ps; [ ]; + "camera.foscam" = ps: with ps; [ ]; + "device_tracker.mikrotik" = ps: with ps; [ ]; + "media_player.soundtouch" = ps: with ps; [ libsoundtouch ]; + "light.lifx_legacy" = ps: with ps; [ ]; + "light.osramlightify" = ps: with ps; [ ]; + "light.limitlessled" = ps: with ps; [ ]; + "linode" = ps: with ps; [ linode-api ]; + "media_player.liveboxplaytv" = ps: with ps; [ ]; + "lametric" = ps: with ps; [ ]; + "notify.lametric" = ps: with ps; [ ]; + "sensor.luftdaten" = ps: with ps; [ ]; + "sensor.lyft" = ps: with ps; [ ]; + "notify.matrix" = ps: with ps; [ matrix-client ]; + "maxcube" = ps: with ps; [ ]; + "notify.message_bird" = ps: with ps; [ ]; + "sensor.mfi" = ps: with ps; [ ]; + "switch.mfi" = ps: with ps; [ ]; + "sensor.miflora" = ps: with ps; [ ]; + "upnp" = ps: with ps; [ ]; + "sensor.mopar" = ps: with ps; [ ]; + "tts" = ps: with ps; [ mutagen ]; + "mychevy" = ps: with ps; [ ]; + "mycroft" = ps: with ps; [ ]; + "usps" = ps: with ps; [ ]; + "media_player.nad" = ps: with ps; [ ]; + "media_player.nadtcp" = ps: with ps; [ ]; + "discovery" = ps: with ps; [ netdisco ]; + "sensor.neurio_energy" = ps: with ps; [ ]; + "sensor.nederlandse_spoorwegen" = ps: with ps; [ ]; + "nuheat" = ps: with ps; [ ]; + "binary_sensor.trend" = ps: with ps; [ numpy ]; + "image_processing.opencv" = ps: with ps; [ numpy ]; + "climate.oem" = ps: with ps; [ ]; + "media_player.onkyo" = ps: with ps; [ ]; + "sensor.openevse" = ps: with ps; [ ]; + "media_player.openhome" = ps: with ps; [ ]; + "switch.orvibo" = ps: with ps; [ ]; + "mqtt" = ps: with ps; [ paho-mqtt ]; + "shiftr" = ps: with ps; [ paho-mqtt ]; + "media_player.panasonic_viera" = ps: with ps; [ ]; + "media_player.dunehd" = ps: with ps; [ ]; + "device_tracker.aruba" = ps: with ps; [ pexpect ]; + "device_tracker.asuswrt" = ps: with ps; [ pexpect ]; + "device_tracker.cisco_ios" = ps: with ps; [ pexpect ]; + "device_tracker.unifi_direct" = ps: with ps; [ pexpect ]; + "media_player.pandora" = ps: with ps; [ pexpect ]; + "hue" = ps: with ps; [ ]; + "rpi_pfio" = ps: with ps; [ ]; + "light.piglow" = ps: with ps; [ ]; + "pilight" = ps: with ps; [ ]; + "dominos" = ps: with ps; [ ]; + "media_player.plex" = ps: with ps; [ ]; + "sensor.plex" = ps: with ps; [ ]; + "sensor.mhz19" = ps: with ps; [ ]; + "sensor.serial_pm" = ps: with ps; [ ]; + "sensor.pocketcasts" = ps: with ps; [ ]; + "climate.proliphix" = ps: with ps; [ ]; + "prometheus" = ps: with ps; [ ]; + "sensor.systemmonitor" = ps: with ps; [ psutil ]; + "wink" = ps: with ps; [ ]; + "notify.pushbullet" = ps: with ps; [ pushbullet ]; + "sensor.pushbullet" = ps: with ps; [ pushbullet ]; + "notify.pushetta" = ps: with ps; [ ]; + "light.rpi_gpio_pwm" = ps: with ps; [ ]; + "canary" = ps: with ps; [ ]; + "sensor.cpuspeed" = ps: with ps; [ ]; + "camera.synology" = ps: with ps; [ ]; + "hdmi_cec" = ps: with ps; [ ]; + "light.tplink" = ps: with ps; [ ]; + "switch.tplink" = ps: with ps; [ ]; + "rfxtrx" = ps: with ps; [ ]; + "sensor.tibber" = ps: with ps; [ ]; + "switch.dlink" = ps: with ps; [ ]; + "ads" = ps: with ps; [ ]; + "sensor.airvisual" = ps: with ps; [ ]; + "alarm_control_panel.alarmdotcom" = ps: with ps; [ ]; + "arlo" = ps: with ps; [ ]; + "apple_tv" = ps: with ps; [ ]; + "device_tracker.bbox" = ps: with ps; [ ]; + "sensor.bbox" = ps: with ps; [ ]; + "device_tracker.bluetooth_tracker" = ps: with ps; [ ]; + "media_player.cast" = ps: with ps; [ PyChromecast ]; + "media_player.cmus" = ps: with ps; [ ]; + "comfoconnect" = ps: with ps; [ ]; + "tts.microsoft" = ps: with ps; [ ]; + "sensor.cups" = ps: with ps; [ ]; + "daikin" = ps: with ps; [ ]; + "climate.daikin" = ps: with ps; [ ]; + "deconz" = ps: with ps; [ ]; + "zwave" = ps: with ps; [ pydispatcher ]; + "android_ip_webcam" = ps: with ps; [ ]; + "sensor.ebox" = ps: with ps; [ ]; + "climate.econet" = ps: with ps; [ ]; + "eight_sleep" = ps: with ps; [ ]; + "media_player.emby" = ps: with ps; [ ]; + "envisalink" = ps: with ps; [ ]; + "climate.ephember" = ps: with ps; [ ]; + "sensor.fido" = ps: with ps; [ ]; + "climate.flexit" = ps: with ps; [ ]; + "ifttt" = ps: with ps; [ ]; + "remote.harmony" = ps: with ps; [ ]; + "binary_sensor.hikvision" = ps: with ps; [ ]; + "hive" = ps: with ps; [ ]; + "homematic" = ps: with ps; [ pyhomematic ]; + "sensor.hydroquebec" = ps: with ps; [ ]; + "alarm_control_panel.ialarm" = ps: with ps; [ ]; + "device_tracker.icloud" = ps: with ps; [ ]; + "sensor.irish_rail_transport" = ps: with ps; [ ]; + "binary_sensor.iss" = ps: with ps; [ ]; + "remote.itach" = ps: with ps; [ ]; + "kira" = ps: with ps; [ ]; + "sensor.kwb" = ps: with ps; [ ]; + "sensor.lacrosse" = ps: with ps; [ ]; + "sensor.lastfm" = ps: with ps; [ pylast ]; + "media_player.webostv" = ps: with ps; [ websockets ]; + "notify.webostv" = ps: with ps; [ ]; + "litejet" = ps: with ps; [ ]; + "sensor.loopenergy" = ps: with ps; [ ]; + "lutron_caseta" = ps: with ps; [ ]; + "lutron" = ps: with ps; [ ]; + "notify.mailgun" = ps: with ps; [ ]; + "mochad" = ps: with ps; [ ]; + "modbus" = ps: with ps; [ ]; + "media_player.monoprice" = ps: with ps; [ ]; + "media_player.yamaha_musiccast" = ps: with ps; [ ]; + "cover.myq" = ps: with ps; [ ]; + "mysensors" = ps: with ps; [ ]; + "lock.nello" = ps: with ps; [ ]; + "device_tracker.netgear" = ps: with ps; [ ]; + "switch.netio" = ps: with ps; [ ]; + "lock.nuki" = ps: with ps; [ ]; + "sensor.nut" = ps: with ps; [ ]; + "alarm_control_panel.nx584" = ps: with ps; [ ]; + "binary_sensor.nx584" = ps: with ps; [ ]; + "iota" = ps: with ps; [ ]; + "sensor.otp" = ps: with ps; [ ]; + "sensor.openweathermap" = ps: with ps; [ ]; + "weather.openweathermap" = ps: with ps; [ ]; + "qwikswitch" = ps: with ps; [ ]; + "rainbird" = ps: with ps; [ ]; + "climate.sensibo" = ps: with ps; [ ]; + "sensor.serial" = ps: with ps; [ ]; + "switch.acer_projector" = ps: with ps; [ pyserial ]; + "lock.sesame" = ps: with ps; [ ]; + "sensor.sma" = ps: with ps; [ ]; + "device_tracker.snmp" = ps: with ps; [ pysnmp ]; + "sensor.snmp" = ps: with ps; [ pysnmp ]; + "switch.snmp" = ps: with ps; [ pysnmp ]; + "sensor.thinkingcleaner" = ps: with ps; [ ]; + "switch.thinkingcleaner" = ps: with ps; [ ]; + "sensor.blockchain" = ps: with ps; [ ]; + "media_player.clementine" = ps: with ps; [ ]; + "digital_ocean" = ps: with ps; [ digital-ocean ]; + "ecobee" = ps: with ps; [ ]; + "climate.eq3btsmart" = ps: with ps; [ ]; + "sensor.etherscan" = ps: with ps; [ ]; + "sensor.darksky" = ps: with ps; [ ]; + "weather.darksky" = ps: with ps; [ ]; + "gc100" = ps: with ps; [ ]; + "sensor.hp_ilo" = ps: with ps; [ ]; + "joaoapps_join" = ps: with ps; [ ]; + "notify.joaoapps_join" = ps: with ps; [ ]; + "juicenet" = ps: with ps; [ ]; + "lirc" = ps: with ps; [ ]; + "fan.xiaomi_miio" = ps: with ps; [ ]; + "light.xiaomi_miio" = ps: with ps; [ ]; + "switch.xiaomi_miio" = ps: with ps; [ ]; + "vacuum.xiaomi_miio" = ps: with ps; [ ]; + "media_player.mpd" = ps: with ps; [ ]; + "light.mystrom" = ps: with ps; [ ]; + "switch.mystrom" = ps: with ps; [ ]; + "nest" = ps: with ps; [ ]; + "device_tracker.nmap_tracker" = ps: with ps; [ ]; + "notify.pushover" = ps: with ps; [ ]; + "sensor.ripple" = ps: with ps; [ ]; + "media_player.roku" = ps: with ps; [ ]; + "sensor.sochain" = ps: with ps; [ ]; + "sensor.synologydsm" = ps: with ps; [ ]; + "tado" = ps: with ps; [ ]; + "telegram_bot" = ps: with ps; [ ]; + "sensor.twitch" = ps: with ps; [ ]; + "velbus" = ps: with ps; [ ]; + "media_player.vlc" = ps: with ps; [ ]; + "sensor.swiss_public_transport" = ps: with ps; [ ]; + "alarm_control_panel.egardia" = ps: with ps; [ ]; + "sensor.whois" = ps: with ps; [ ]; + "device_tracker.tile" = ps: with ps; [ ]; + "climate.touchline" = ps: with ps; [ ]; + "device_tracker.trackr" = ps: with ps; [ ]; + "tradfri" = ps: with ps; [ ]; + "device_tracker.unifi" = ps: with ps; [ ]; + "keyboard" = ps: with ps; [ ]; + "vera" = ps: with ps; [ ]; + "media_player.vizio" = ps: with ps; [ ]; + "velux" = ps: with ps; [ ]; + "wemo" = ps: with ps; [ ]; + "camera.xeoma" = ps: with ps; [ ]; + "zabbix" = ps: with ps; [ ]; + "sensor.qnap" = ps: with ps; [ ]; + "switch.rachio" = ps: with ps; [ ]; + "climate.radiotherm" = ps: with ps; [ ]; + "raincloud" = ps: with ps; [ ]; + "raspihats" = ps: with ps; [ ]; + "switch.rainmachine" = ps: with ps; [ ]; + "python_script" = ps: with ps; [ ]; + "rflink" = ps: with ps; [ ]; + "ring" = ps: with ps; [ ]; + "notify.rocketchat" = ps: with ps; [ ]; + "vacuum.roomba" = ps: with ps; [ ]; + "switch.rpi_rf" = ps: with ps; [ ]; + "media_player.russound_rnet" = ps: with ps; [ ]; + "media_player.russound_rio" = ps: with ps; [ ]; + "media_player.yamaha" = ps: with ps; [ ]; + "media_player.samsungtv" = ps: with ps; [ ]; + "satel_integra" = ps: with ps; [ ]; + "sensor.deutsche_bahn" = ps: with ps; [ ]; + "scsgate" = ps: with ps; [ ]; + "notify.sendgrid" = ps: with ps; [ ]; + "light.sensehat" = ps: with ps; [ ]; + "sensor.sensehat" = ps: with ps; [ ]; + "media_player.aquostv" = ps: with ps; [ ]; + "sensor.shodan" = ps: with ps; [ ]; + "notify.simplepush" = ps: with ps; [ ]; + "alarm_control_panel.simplisafe" = ps: with ps; [ ]; + "skybell" = ps: with ps; [ ]; + "notify.slack" = ps: with ps; [ ]; + "sleepiq" = ps: with ps; [ ]; + "media_player.snapcast" = ps: with ps; [ ]; + "sensor.speedtest" = ps: with ps; [ ]; + "recorder" = ps: with ps; [ sqlalchemy ]; + "statsd" = ps: with ps; [ statsd ]; + "sensor.steam_online" = ps: with ps; [ ]; + "tahoma" = ps: with ps; [ ]; + "sensor.tank_utility" = ps: with ps; [ ]; + "binary_sensor.tapsaff" = ps: with ps; [ ]; + "tellstick" = ps: with ps; [ ]; + "tellduslive" = ps: with ps; [ ]; + "sensor.temper" = ps: with ps; [ ]; + "tesla" = ps: with ps; [ ]; + "thingspeak" = ps: with ps; [ ]; + "light.tikteck" = ps: with ps; [ ]; + "calendar.todoist" = ps: with ps; [ todoist ]; + "toon" = ps: with ps; [ ]; + "alarm_control_panel.totalconnect" = ps: with ps; [ ]; + "sensor.transmission" = ps: with ps; [ transmissionrpc ]; + "switch.transmission" = ps: with ps; [ transmissionrpc ]; + "twilio" = ps: with ps; [ twilio ]; + "sensor.uber" = ps: with ps; [ ]; + "sensor.ups" = ps: with ps; [ ]; + "camera.uvc" = ps: with ps; [ ]; + "climate.venstar" = ps: with ps; [ ]; + "volvooncall" = ps: with ps; [ ]; + "sensor.vasttrafik" = ps: with ps; [ ]; + "vultr" = ps: with ps; [ vultr ]; + "wake_on_lan" = ps: with ps; [ ]; + "switch.wake_on_lan" = ps: with ps; [ ]; + "sensor.waqi" = ps: with ps; [ ]; + "cloud" = ps: with ps; [ ]; + "waterfurnace" = ps: with ps; [ ]; + "media_player.gpmdp" = ps: with ps; [ ]; + "spc" = ps: with ps; [ websockets ]; + "zigbee" = ps: with ps; [ ]; + "sensor.xbox_live" = ps: with ps; [ ]; + "knx" = ps: with ps; [ ]; + "media_player.bluesound" = ps: with ps; [ xmltodict ]; + "sensor.swiss_hydrological_data" = ps: with ps; [ xmltodict ]; + "sensor.ted5000" = ps: with ps; [ xmltodict ]; + "sensor.yr" = ps: with ps; [ xmltodict ]; + "sensor.yahoo_finance" = ps: with ps; [ ]; + "sensor.yweather" = ps: with ps; [ ]; + "weather.yweather" = ps: with ps; [ ]; + "light.yeelight" = ps: with ps; [ ]; + "light.yeelightsunflower" = ps: with ps; [ ]; + "media_extractor" = ps: with ps; [ ]; + "light.zengge" = ps: with ps; [ ]; + "zeroconf" = ps: with ps; [ zeroconf ]; + "media_player.ziggo_mediabox_xl" = ps: with ps; [ ]; + }; +} diff --git a/pkgs/servers/home-assistant/default.nix b/pkgs/servers/home-assistant/default.nix index 9ec314e5c737..bce0369cb529 100644 --- a/pkgs/servers/home-assistant/default.nix +++ b/pkgs/servers/home-assistant/default.nix @@ -1,4 +1,5 @@ { stdenv, fetchFromGitHub, python3 +, extraComponents ? [] , extraPackages ? ps: [] , skipPip ? true }: @@ -24,14 +25,25 @@ let }; }; + componentPackages = import ./component-packages.nix; + + availableComponents = builtins.attrNames componentPackages.components; + + getPackages = component: builtins.getAttr component componentPackages.components; + + componentBuildInputs = map (component: getPackages component py.pkgs) extraComponents; + # Ensure that we are using a consistent package set extraBuildInputs = extraPackages py.pkgs; + # Don't forget to run parse-requirements.py after updating + hassVersion = "0.62.1"; + in with py.pkgs; buildPythonApplication rec { pname = "homeassistant"; - version = "0.62.1"; + version = assert (componentPackages.version == hassVersion); hassVersion; - diabled = !isPy3k; + inherit availableComponents; # PyPI tarball is missing tests/ directory src = fetchFromGitHub { @@ -45,8 +57,8 @@ in with py.pkgs; buildPythonApplication rec { # From setup.py requests pyyaml pytz pip jinja2 voluptuous typing aiohttp yarl async-timeout chardet astral certifi # From the components that are part of the default configuration.yaml - sqlalchemy aiohttp-cors hass-frontend user-agents distro mutagen xmltodict netdisco - ] ++ extraBuildInputs; + sqlalchemy aiohttp-cors hass-frontend user-agents distro mutagen xmltodict netdisco + ] ++ componentBuildInputs ++ extraBuildInputs; checkInputs = [ pytest requests-mock pydispatcher pytest-aiohttp diff --git a/pkgs/servers/home-assistant/parse-requirements.py b/pkgs/servers/home-assistant/parse-requirements.py new file mode 100755 index 000000000000..aa293921e871 --- /dev/null +++ b/pkgs/servers/home-assistant/parse-requirements.py @@ -0,0 +1,97 @@ +#! /usr/bin/env nix-shell +#! nix-shell -i python3 -p "python3.withPackages (ps: with ps; [ setuptools ])" +# +# This script downloads https://github.com/home-assistant/home-assistant/blob/master/requirements_all.txt. +# This file contains lines of the form +# +# # homeassistant.components.foo +# # homeassistant.components.bar +# foobar==1.2.3 +# +# i.e. it lists dependencies and the components that require them. +# By parsing the file, a dictionary mapping component to dependencies is created. +# For all of these dependencies, Nixpkgs' python3Packages are searched for appropriate names. +# Then, a Nix attribute set mapping component name to dependencies is created. + +from urllib.request import urlopen +import subprocess +import os +import sys +import json +import re +from pkg_resources import Requirement, RequirementParseError + +PREFIX = '# homeassistant.components.' +PKG_SET = 'python3Packages' + +def get_version(): + with open(os.path.dirname(sys.argv[0]) + '/default.nix') as f: + m = re.search('hassVersion = "([\\d\\.]+)";', f.read()) + return m.group(1) + +def fetch_reqs(version='master'): + requirements = {} + with urlopen('https://github.com/home-assistant/home-assistant/raw/{}/requirements_all.txt'.format(version)) as response: + components = [] + for line in response.read().decode().splitlines(): + if line == '': + components = [] + elif line[:len(PREFIX)] == PREFIX: + component = line[len(PREFIX):] + components.append(component) + if component not in requirements: + requirements[component] = [] + elif line[0] != '#': + for component in components: + requirements[component].append(line) + return requirements + +# Store a JSON dump of Nixpkgs' python3Packages +output = subprocess.check_output(['nix-env', '-f', os.path.dirname(sys.argv[0]) + '/../../..', '-qa', '-A', PKG_SET, '--json']) +packages = json.loads(output) + +def name_to_attr_path(req): + attr_paths = [] + pattern = re.compile('python3\\.6-{}-\\d'.format(req), re.I) + for attr_path, package in packages.items(): + if pattern.match(package['name']): + attr_paths.append(attr_path) + # Let's hope there's only one derivation with a matching name + assert(len(attr_paths) <= 1) + if attr_paths: + return attr_paths[0] + else: + return None + +version = get_version() +requirements = fetch_reqs(version=version) +build_inputs = {} +for component, reqs in requirements.items(): + attr_paths = [] + for req in reqs: + try: + name = Requirement.parse(req).project_name + attr_path = name_to_attr_path(name) + if attr_path is not None: + # Add attribute path without "python3Packages." prefix + attr_paths.append(attr_path[len(PKG_SET + '.'):]) + except RequirementParseError: + continue + else: + build_inputs[component] = attr_paths + +# Only select components which have any dependency +#build_inputs = {k: v for k, v in build_inputs.items() if len(v) > 0} + +with open(os.path.dirname(sys.argv[0]) + '/component-packages.nix', 'w') as f: + f.write('# Generated from parse-requirements.py\n') + f.write('# Do not edit!\n\n') + f.write('{\n') + f.write(' version = "{}";\n'.format(version)) + f.write(' components = {\n') + for component, attr_paths in build_inputs.items(): + f.write(' "{}" = ps: with ps; [ '.format(component)) + f.write(' '.join(attr_paths)) + f.write(' ];\n') + f.write(' };\n') + f.write('}\n')