mirror of
https://github.com/home-assistant/core.git
synced 2026-01-10 17:47:16 +01:00
Compare commits
241 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
a2c9834852 | ||
|
|
4f820aef83 | ||
|
|
6ba2891604 | ||
|
|
26726af689 | ||
|
|
9d21afa444 | ||
|
|
05cdab03b1 | ||
|
|
ece9c62ee8 | ||
|
|
a69080ba73 | ||
|
|
7741ec4d5a | ||
|
|
92457ca5ca | ||
|
|
8777146053 | ||
|
|
c4eab21736 | ||
|
|
f11f5255ae | ||
|
|
a1369c2fee | ||
|
|
8bf5e57b7f | ||
|
|
14ceb8472f | ||
|
|
e022f4465c | ||
|
|
b8e38c1b25 | ||
|
|
c7904a4b37 | ||
|
|
8f3434c2ab | ||
|
|
d13c892b28 | ||
|
|
26d4736ebf | ||
|
|
e670491c86 | ||
|
|
e26a5abb2b | ||
|
|
78e162c1d3 | ||
|
|
9176e13a97 | ||
|
|
217782cd05 | ||
|
|
d8817bb127 | ||
|
|
90c4f6f6e5 | ||
|
|
f795d03503 | ||
|
|
24c7c2aa6e | ||
|
|
2b48ecd5c5 | ||
|
|
71b800457b | ||
|
|
c3f090af17 | ||
|
|
29ad3961e5 | ||
|
|
52437f6246 | ||
|
|
646c4a7137 | ||
|
|
4de2efd07f | ||
|
|
6540114ec5 | ||
|
|
fa9a6f072e | ||
|
|
a55afa8119 | ||
|
|
19d99ddf57 | ||
|
|
1766536812 | ||
|
|
02b12ec1b9 | ||
|
|
80250add9e | ||
|
|
7e3567319f | ||
|
|
afa99c9189 | ||
|
|
7519e8d417 | ||
|
|
133ae63ed0 | ||
|
|
3fddf5df08 | ||
|
|
a27e821e8b | ||
|
|
c71e5ed588 | ||
|
|
2cebf9ef71 | ||
|
|
3cca3c37f0 | ||
|
|
77e7b63f4a | ||
|
|
65432ba552 | ||
|
|
bad0a8b342 | ||
|
|
baa4945944 | ||
|
|
e85b089eff | ||
|
|
a62c116959 | ||
|
|
6fa8fdf555 | ||
|
|
79445a7ccc | ||
|
|
73b38572f0 | ||
|
|
b2ba9d07ca | ||
|
|
8aef8c6bb4 | ||
|
|
0c4380a78d | ||
|
|
42c27e5b72 | ||
|
|
5ad3e75a4d | ||
|
|
6ffe9ad473 | ||
|
|
6a74c403c0 | ||
|
|
2731777c7e | ||
|
|
a59487a438 | ||
|
|
f1a0ad9e4a | ||
|
|
b57d809dad | ||
|
|
f997957054 | ||
|
|
c8048e1aff | ||
|
|
17a96c6d9b | ||
|
|
96133f5e6b | ||
|
|
af4b85d39d | ||
|
|
324a7c7875 | ||
|
|
c59d45caa3 | ||
|
|
f272ed3b91 | ||
|
|
548371e94c | ||
|
|
1aee7a1673 | ||
|
|
b6987a1235 | ||
|
|
d1f75fcf32 | ||
|
|
adca598172 | ||
|
|
7f940423ad | ||
|
|
1b0e523a60 | ||
|
|
0d46e2c0b5 | ||
|
|
d2a83c2732 | ||
|
|
dc64634e21 | ||
|
|
0ae38aece8 | ||
|
|
88df2e0ea5 | ||
|
|
7421156dfc | ||
|
|
d5732c4dba | ||
|
|
eabb68ad7d | ||
|
|
8d1cf553de | ||
|
|
89f8203163 | ||
|
|
ed93c3b2c1 | ||
|
|
6988fe783c | ||
|
|
9214934d47 | ||
|
|
71ebc4f594 | ||
|
|
c5f4aa0466 | ||
|
|
49b92b5349 | ||
|
|
1ddc249989 | ||
|
|
4c4eff1d62 | ||
|
|
16dbf9b2ea | ||
|
|
c68b621972 | ||
|
|
d81df1f0ae | ||
|
|
112ed88d64 | ||
|
|
5f34d3ccb9 | ||
|
|
3c811bbf1a | ||
|
|
90dfe72d31 | ||
|
|
89221bfab9 | ||
|
|
773c567563 | ||
|
|
58c23bc2d9 | ||
|
|
611597a87b | ||
|
|
ecabf92504 | ||
|
|
e9cd9f88be | ||
|
|
a58f1eedda | ||
|
|
ce550206a4 | ||
|
|
1ddc65a0ce | ||
|
|
2b6e197deb | ||
|
|
b125514655 | ||
|
|
c90e13bfef | ||
|
|
30d4a9f12c | ||
|
|
3432c5da9e | ||
|
|
5716d0aa1a | ||
|
|
86a510441c | ||
|
|
bc85d47878 | ||
|
|
72bb94de96 | ||
|
|
0a4251e08f | ||
|
|
1b66520e31 | ||
|
|
81bb928394 | ||
|
|
fe468ace34 | ||
|
|
6526c68e2d | ||
|
|
f64a99878f | ||
|
|
03855c18fc | ||
|
|
2b02c0d0fc | ||
|
|
07dc23a0e3 | ||
|
|
77635d40e2 | ||
|
|
f4102339c1 | ||
|
|
21871b3d6b | ||
|
|
88be786e82 | ||
|
|
4b1de61110 | ||
|
|
ab17b22239 | ||
|
|
423d595edf | ||
|
|
e044eace20 | ||
|
|
9653544144 | ||
|
|
01d8b5831e | ||
|
|
62c2bbd59a | ||
|
|
398281959a | ||
|
|
db07e45df8 | ||
|
|
0344c761fc | ||
|
|
6cb8806085 | ||
|
|
2b250a7ec8 | ||
|
|
08849fd3e8 | ||
|
|
92dc26bab3 | ||
|
|
88669c6543 | ||
|
|
350904870e | ||
|
|
1499485a71 | ||
|
|
bf4b7a82b4 | ||
|
|
c2aa06d0d4 | ||
|
|
188293770e | ||
|
|
12df14b87b | ||
|
|
f195ecca4b | ||
|
|
46ece3603f | ||
|
|
05db444832 | ||
|
|
ecfe0fc3dd | ||
|
|
e5a2ef9b8d | ||
|
|
9591aa66ba | ||
|
|
af473cddf0 | ||
|
|
9c7ef13f91 | ||
|
|
9f96aab2f4 | ||
|
|
ce5cf5803c | ||
|
|
e14b243336 | ||
|
|
29131a655d | ||
|
|
c020b7c47d | ||
|
|
8529ad3ba1 | ||
|
|
0d42ed1861 | ||
|
|
ba923d2d66 | ||
|
|
9b1491a98d | ||
|
|
1aab551eed | ||
|
|
54dfc3e2b4 | ||
|
|
cf5ba7d922 | ||
|
|
d16c507f34 | ||
|
|
54489a3514 | ||
|
|
f5076188ef | ||
|
|
d33cad0b24 | ||
|
|
4423572682 | ||
|
|
c90f0d5bd6 | ||
|
|
0466e43478 | ||
|
|
7807b40925 | ||
|
|
9e4bd88a06 | ||
|
|
b4f8d157d6 | ||
|
|
ade86b9b8d | ||
|
|
179c2315be | ||
|
|
f396de623b | ||
|
|
d0365f5911 | ||
|
|
dbdf5558e6 | ||
|
|
42265036ff | ||
|
|
53b204347d | ||
|
|
dc656205c4 | ||
|
|
087748b5ff | ||
|
|
17ba33004c | ||
|
|
9520d38288 | ||
|
|
cf69f25354 | ||
|
|
101225749b | ||
|
|
e581d9e249 | ||
|
|
3ce50b0a6a | ||
|
|
941f9b29dc | ||
|
|
3b34594aa3 | ||
|
|
5a9e8b2d3e | ||
|
|
89c96279ce | ||
|
|
851378739f | ||
|
|
9575c20b7c | ||
|
|
dcaced1966 | ||
|
|
6a80ffa8cc | ||
|
|
3769f5893a | ||
|
|
62f12d242a | ||
|
|
b25e951dcc | ||
|
|
8d2d71c16a | ||
|
|
4e84e8a15e | ||
|
|
018a5d5c1f | ||
|
|
bd930b6e96 | ||
|
|
ef2e3f607a | ||
|
|
e480f75d6d | ||
|
|
300384410f | ||
|
|
b022428cb6 | ||
|
|
2b25c25ca8 | ||
|
|
4ff8a46acf | ||
|
|
abf2e763b1 | ||
|
|
6381242eca | ||
|
|
e75b12b92a | ||
|
|
95da6d41f9 | ||
|
|
3fcfba0a1e | ||
|
|
2787671de5 | ||
|
|
673c8907e3 | ||
|
|
5ef602bc2e | ||
|
|
acc44aaf6c |
919
.coveragerc
919
.coveragerc
File diff suppressed because it is too large
Load Diff
232
CODEOWNERS
232
CODEOWNERS
@@ -18,7 +18,7 @@ homeassistant/components/frontend/* @home-assistant/core
|
||||
homeassistant/components/group/* @home-assistant/core
|
||||
homeassistant/components/history/* @home-assistant/core
|
||||
homeassistant/components/http/* @home-assistant/core
|
||||
homeassistant/components/input_*.py @home-assistant/core
|
||||
homeassistant/components/input_*/* @home-assistant/core
|
||||
homeassistant/components/introduction/* @home-assistant/core
|
||||
homeassistant/components/logger/* @home-assistant/core
|
||||
homeassistant/components/lovelace/* @home-assistant/core
|
||||
@@ -47,123 +47,58 @@ homeassistant/components/*/zwave.py @home-assistant/z-wave
|
||||
homeassistant/components/hassio/* @home-assistant/hassio
|
||||
|
||||
# Individual platforms
|
||||
homeassistant/components/alarm_control_panel/manual_mqtt.py @colinodell
|
||||
homeassistant/components/binary_sensor/hikvision.py @mezz64
|
||||
homeassistant/components/binary_sensor/threshold.py @fabaff
|
||||
homeassistant/components/binary_sensor/uptimerobot.py @ludeeus
|
||||
homeassistant/components/camera/push.py @dgomes
|
||||
homeassistant/components/camera/yi.py @bachya
|
||||
homeassistant/components/climate/coolmaster.py @OnFreund
|
||||
homeassistant/components/climate/ephember.py @ttroy50
|
||||
homeassistant/components/climate/eq3btsmart.py @rytilahti
|
||||
homeassistant/components/climate/mill.py @danielhiversen
|
||||
homeassistant/components/climate/sensibo.py @andrey-git
|
||||
homeassistant/components/cover/brunt.py @eavanvalkenburg
|
||||
homeassistant/components/cover/group.py @cdce8p
|
||||
homeassistant/components/cover/template.py @PhracturedBlue
|
||||
homeassistant/components/device_tracker/asuswrt.py @kennedyshead
|
||||
homeassistant/components/device_tracker/automatic.py @armills
|
||||
homeassistant/components/device_tracker/bt_smarthub.py @jxwolstenholme
|
||||
homeassistant/components/device_tracker/huawei_router.py @abmantis
|
||||
homeassistant/components/device_tracker/quantum_gateway.py @cisasteelersfan
|
||||
homeassistant/components/device_tracker/tile.py @bachya
|
||||
homeassistant/components/device_tracker/traccar.py @ludeeus
|
||||
homeassistant/components/device_tracker/synology_srm.py @aerialls
|
||||
homeassistant/components/device_tracker/xfinity.py @cisasteelersfan
|
||||
homeassistant/components/light/lifx_legacy.py @amelchio
|
||||
homeassistant/components/light/yeelight.py @rytilahti
|
||||
homeassistant/components/light/yeelightsunflower.py @lindsaymarkward
|
||||
homeassistant/components/lock/nello.py @pschmitt
|
||||
homeassistant/components/lock/nuki.py @pschmitt
|
||||
homeassistant/components/media_player/emby.py @mezz64
|
||||
homeassistant/components/media_player/kodi.py @armills
|
||||
homeassistant/components/media_player/liveboxplaytv.py @pschmitt
|
||||
homeassistant/components/media_player/mediaroom.py @dgomes
|
||||
homeassistant/components/media_player/monoprice.py @etsinko
|
||||
homeassistant/components/media_player/mpd.py @fabaff
|
||||
homeassistant/components/media_player/xiaomi_tv.py @fattdev
|
||||
homeassistant/components/media_player/yamaha_musiccast.py @jalmeroth
|
||||
homeassistant/components/notify/aws_lambda.py @robbiet480
|
||||
homeassistant/components/notify/aws_sns.py @robbiet480
|
||||
homeassistant/components/notify/aws_sqs.py @robbiet480
|
||||
homeassistant/components/notify/file.py @fabaff
|
||||
homeassistant/components/notify/flock.py @fabaff
|
||||
homeassistant/components/notify/gntp.py @robbiet480
|
||||
homeassistant/components/notify/html5.py @robbiet480
|
||||
homeassistant/components/notify/mastodon.py @fabaff
|
||||
homeassistant/components/notify/smtp.py @fabaff
|
||||
homeassistant/components/notify/syslog.py @fabaff
|
||||
homeassistant/components/notify/twilio_call.py @robbiet480
|
||||
homeassistant/components/notify/twilio_sms.py @robbiet480
|
||||
homeassistant/components/notify/xmpp.py @fabaff
|
||||
homeassistant/components/notify/yessssms.py @flowolf
|
||||
homeassistant/components/scene/lifx_cloud.py @amelchio
|
||||
homeassistant/components/sensor/airvisual.py @bachya
|
||||
homeassistant/components/sensor/alpha_vantage.py @fabaff
|
||||
homeassistant/components/sensor/bitcoin.py @fabaff
|
||||
homeassistant/components/sensor/cpuspeed.py @fabaff
|
||||
homeassistant/components/sensor/cups.py @fabaff
|
||||
homeassistant/components/sensor/darksky.py @fabaff
|
||||
homeassistant/components/sensor/discogs.py @thibmaek
|
||||
homeassistant/components/sensor/file.py @fabaff
|
||||
homeassistant/components/sensor/filter.py @dgomes
|
||||
homeassistant/components/sensor/fixer.py @fabaff
|
||||
homeassistant/components/sensor/flunearyou.py @bachya
|
||||
homeassistant/components/sensor/gearbest.py @HerrHofrat
|
||||
homeassistant/components/sensor/gitter.py @fabaff
|
||||
homeassistant/components/sensor/glances.py @fabaff
|
||||
homeassistant/components/sensor/gpsd.py @fabaff
|
||||
homeassistant/components/sensor/integration.py @dgomes
|
||||
homeassistant/components/sensor/irish_rail_transport.py @ttroy50
|
||||
homeassistant/components/sensor/jewish_calendar.py @tsvi
|
||||
homeassistant/components/sensor/launch_library.py @ludeeus
|
||||
homeassistant/components/sensor/linux_battery.py @fabaff
|
||||
homeassistant/components/sensor/miflora.py @danielhiversen @ChristianKuehnel
|
||||
homeassistant/components/sensor/min_max.py @fabaff
|
||||
homeassistant/components/sensor/moon.py @fabaff
|
||||
homeassistant/components/sensor/netdata.py @fabaff
|
||||
homeassistant/components/sensor/nmbs.py @thibmaek
|
||||
homeassistant/components/sensor/nsw_fuel_station.py @nickw444
|
||||
homeassistant/components/sensor/pi_hole.py @fabaff
|
||||
homeassistant/components/sensor/pollen.py @bachya
|
||||
homeassistant/components/sensor/pvoutput.py @fabaff
|
||||
homeassistant/components/sensor/qnap.py @colinodell
|
||||
homeassistant/components/sensor/ruter.py @ludeeus
|
||||
homeassistant/components/sensor/scrape.py @fabaff
|
||||
homeassistant/components/sensor/serial.py @fabaff
|
||||
homeassistant/components/sensor/seventeentrack.py @bachya
|
||||
homeassistant/components/sensor/shodan.py @fabaff
|
||||
homeassistant/components/sensor/sma.py @kellerza
|
||||
homeassistant/components/sensor/sql.py @dgomes
|
||||
homeassistant/components/sensor/statistics.py @fabaff
|
||||
homeassistant/components/sensor/swiss*.py @fabaff
|
||||
homeassistant/components/sensor/sytadin.py @gautric
|
||||
homeassistant/components/sensor/tautulli.py @ludeeus
|
||||
homeassistant/components/sensor/time_date.py @fabaff
|
||||
homeassistant/components/sensor/version.py @fabaff
|
||||
homeassistant/components/sensor/waqi.py @andrey-git
|
||||
homeassistant/components/sensor/worldclock.py @fabaff
|
||||
homeassistant/components/switch/switchbot.py @danielhiversen
|
||||
homeassistant/components/switch/switchmate.py @danielhiversen
|
||||
homeassistant/components/vacuum/roomba.py @pschmitt
|
||||
homeassistant/components/weather/__init__.py @fabaff
|
||||
homeassistant/components/weather/darksky.py @fabaff
|
||||
homeassistant/components/weather/demo.py @fabaff
|
||||
homeassistant/components/weather/met.py @danielhiversen
|
||||
homeassistant/components/weather/openweathermap.py @fabaff
|
||||
homeassistant/components/tts/amazon_polly.py @robbiet480
|
||||
|
||||
# A
|
||||
homeassistant/components/airvisual/sensor.py @bachya
|
||||
homeassistant/components/alarm_control_panel/manual_mqtt.py @colinodell
|
||||
homeassistant/components/alpha_vantage/sensor.py @fabaff
|
||||
homeassistant/components/ambient_station/* @bachya
|
||||
homeassistant/components/arduino/* @fabaff
|
||||
homeassistant/components/arest/* @fabaff
|
||||
homeassistant/components/asuswrt/device_tracker.py @kennedyshead
|
||||
homeassistant/components/automatic/device_tracker.py @armills
|
||||
homeassistant/components/axis/* @kane610
|
||||
homeassistant/components/*/arest.py @fabaff
|
||||
|
||||
# B
|
||||
homeassistant/components/bitcoin/sensor.py @fabaff
|
||||
homeassistant/components/blink/* @fronzbot
|
||||
homeassistant/components/bmw_connected_drive/* @ChristianKuehnel
|
||||
homeassistant/components/*/broadlink.py @danielhiversen
|
||||
homeassistant/components/braviatv/media_player.py @robbiet480
|
||||
homeassistant/components/broadlink/* @danielhiversen
|
||||
homeassistant/components/brunt/cover.py @eavanvalkenburg
|
||||
homeassistant/components/bt_smarthub/device_tracker.py @jxwolstenholme
|
||||
|
||||
# C
|
||||
homeassistant/components/cloudflare/* @ludeeus
|
||||
homeassistant/components/coolmaster/climate.py @OnFreund
|
||||
homeassistant/components/counter/* @fabaff
|
||||
homeassistant/components/cover/group.py @cdce8p
|
||||
homeassistant/components/cpuspeed/sensor.py @fabaff
|
||||
homeassistant/components/cups/sensor.py @fabaff
|
||||
|
||||
# D
|
||||
homeassistant/components/daikin/* @fredrike @rofrantz
|
||||
homeassistant/components/darksky/* @fabaff
|
||||
homeassistant/components/discogs/sensor.py @thibmaek
|
||||
homeassistant/components/deconz/* @kane610
|
||||
homeassistant/components/demo/weather.py @fabaff
|
||||
homeassistant/components/digital_ocean/* @fabaff
|
||||
homeassistant/components/doorbird/* @oblogic7
|
||||
homeassistant/components/dweet/* @fabaff
|
||||
|
||||
# E
|
||||
@@ -171,94 +106,185 @@ homeassistant/components/ecovacs/* @OverloadUT
|
||||
homeassistant/components/edp_redy/* @abmantis
|
||||
homeassistant/components/eight_sleep/* @mezz64
|
||||
homeassistant/components/egardia/* @jeroenterheerdt
|
||||
homeassistant/components/esphome/*.py @OttoWinter
|
||||
homeassistant/components/emby/media_player.py @mezz64
|
||||
homeassistant/components/ephember/climate.py @ttroy50
|
||||
homeassistant/components/eq3btsmart/climate.py @rytilahti
|
||||
homeassistant/components/esphome/* @OttoWinter
|
||||
|
||||
# F
|
||||
homeassistant/components/freebox/*.py @snoof85
|
||||
homeassistant/components/file/sensor.py @fabaff
|
||||
homeassistant/components/filter/sensor.py @dgomes
|
||||
homeassistant/components/fitbit/sensor.py @robbiet480
|
||||
homeassistant/components/fixer/sensor.py @fabaff
|
||||
homeassistant/components/flunearyou/sensor.py @bachya
|
||||
homeassistant/components/foursquare/* @robbiet480
|
||||
homeassistant/components/freebox/* @snoof85
|
||||
|
||||
# G
|
||||
homeassistant/components/gearbest/sensor.py @HerrHofrat
|
||||
homeassistant/components/gitter/sensor.py @fabaff
|
||||
homeassistant/components/glances/sensor.py @fabaff
|
||||
homeassistant/components/google_travel_time/sensor.py @robbiet480
|
||||
homeassistant/components/googlehome/* @ludeeus
|
||||
homeassistant/components/gpsd/sensor.py @fabaff
|
||||
homeassistant/components/gtfs/sensor.py @robbiet480
|
||||
|
||||
# H
|
||||
homeassistant/components/harmony/* @ehendrix23
|
||||
homeassistant/components/hikvision/binary_sensor.py @mezz64
|
||||
homeassistant/components/history_graph/* @andrey-git
|
||||
homeassistant/components/hive/* @Rendili @KJonline
|
||||
homeassistant/components/homekit/* @cdce8p
|
||||
homeassistant/components/huawei_lte/* @scop
|
||||
homeassistant/components/huawei_router/device_tracker.py @abmantis
|
||||
|
||||
# I
|
||||
homeassistant/components/influx/* @fabaff
|
||||
homeassistant/components/influxdb/* @fabaff
|
||||
homeassistant/components/integration/sensor.py @dgomes
|
||||
homeassistant/components/ios/* @robbiet480
|
||||
homeassistant/components/ipma/* @dgomes
|
||||
homeassistant/components/irish_rail_transport/sensor.py @ttroy50
|
||||
|
||||
# J
|
||||
homeassistant/components/jewish_calendar/sensor.py @tsvi
|
||||
|
||||
# K
|
||||
homeassistant/components/knx/* @Julius2342
|
||||
homeassistant/components/kodi/media_player.py @armills
|
||||
homeassistant/components/konnected/* @heythisisnate
|
||||
|
||||
# L
|
||||
homeassistant/components/lametric/notify.py @robbiet480
|
||||
homeassistant/components/launch_library/sensor.py @ludeeus
|
||||
homeassistant/components/lifx/* @amelchio
|
||||
homeassistant/components/lifx_cloud/scene.py @amelchio
|
||||
homeassistant/components/lifx_legacy/light.py @amelchio
|
||||
homeassistant/components/linux_battery/sensor.py @fabaff
|
||||
homeassistant/components/liveboxplaytv/media_player.py @pschmitt
|
||||
homeassistant/components/luftdaten/* @fabaff
|
||||
|
||||
# M
|
||||
homeassistant/components/matrix/* @tinloaf
|
||||
homeassistant/components/mediaroom/media_player.py @dgomes
|
||||
homeassistant/components/melissa/* @kennedyshead
|
||||
homeassistant/components/*/melissa.py @kennedyshead
|
||||
homeassistant/components/*/mystrom.py @fabaff
|
||||
homeassistant/components/met/weather.py @danielhiversen
|
||||
homeassistant/components/miflora/sensor.py @danielhiversen @ChristianKuehnel
|
||||
homeassistant/components/mill/climate.py @danielhiversen
|
||||
homeassistant/components/min_max/sensor.py @fabaff
|
||||
homeassistant/components/mobile_app/* @robbiet480
|
||||
homeassistant/components/monoprice/media_player.py @etsinko
|
||||
homeassistant/components/moon/sensor.py @fabaff
|
||||
homeassistant/components/mpd/media_player.py @fabaff
|
||||
homeassistant/components/mystrom/* @fabaff
|
||||
|
||||
# N
|
||||
homeassistant/components/nello/lock.py @pschmitt
|
||||
homeassistant/components/ness_alarm/* @nickw444
|
||||
homeassistant/components/*/ness_alarm.py @nickw444
|
||||
homeassistant/components/netdata/sensor.py @fabaff
|
||||
homeassistant/components/nissan_leaf/* @filcole
|
||||
homeassistant/components/nmbs/sensor.py @thibmaek
|
||||
homeassistant/components/no_ip/* @fabaff
|
||||
homeassistant/components/nuki/lock.py @pschmitt
|
||||
homeassistant/components/nsw_fuel_station/sensor.py @nickw444
|
||||
|
||||
# O
|
||||
homeassistant/components/ohmconnect/sensor.py @robbiet480
|
||||
homeassistant/components/openuv/* @bachya
|
||||
homeassistant/components/openweathermap/weather.py @fabaff
|
||||
homeassistant/components/owlet/* @oblogic7
|
||||
|
||||
# P
|
||||
homeassistant/components/pi_hole/sensor.py @fabaff
|
||||
homeassistant/components/plant/* @ChristianKuehnel
|
||||
homeassistant/components/point/* @fredrike
|
||||
homeassistant/components/pollen/sensor.py @bachya
|
||||
homeassistant/components/push/camera.py @dgomes
|
||||
homeassistant/components/pvoutput/sensor.py @fabaff
|
||||
|
||||
# Q
|
||||
homeassistant/components/qnap/sensor.py @colinodell
|
||||
homeassistant/components/quantum_gateway/device_tracker.py @cisasteelersfan
|
||||
homeassistant/components/qwikswitch/* @kellerza
|
||||
|
||||
# R
|
||||
homeassistant/components/rainmachine/* @bachya
|
||||
homeassistant/components/random/* @fabaff
|
||||
homeassistant/components/rfxtrx/* @danielhiversen
|
||||
homeassistant/components/*/random.py @fabaff
|
||||
homeassistant/components/rmvtransport/* @cgtobi
|
||||
homeassistant/components/roomba/vacuum.py @pschmitt
|
||||
homeassistant/components/ruter/sensor.py @ludeeus
|
||||
|
||||
# S
|
||||
homeassistant/components/scrape/sensor.py @fabaff
|
||||
homeassistant/components/sensibo/climate.py @andrey-git
|
||||
homeassistant/components/serial/sensor.py @fabaff
|
||||
homeassistant/components/seventeentrack/sensor.py @bachya
|
||||
homeassistant/components/shiftr/* @fabaff
|
||||
homeassistant/components/shodan/sensor.py @fabaff
|
||||
homeassistant/components/simplisafe/* @bachya
|
||||
homeassistant/components/sma/sensor.py @kellerza
|
||||
homeassistant/components/smartthings/* @andrewsayre
|
||||
homeassistant/components/sonos/* @amelchio
|
||||
homeassistant/components/spaceapi/* @fabaff
|
||||
homeassistant/components/spider/* @peternijssen
|
||||
homeassistant/components/sql/sensor.py @dgomes
|
||||
homeassistant/components/statistics/sensor.py @fabaff
|
||||
homeassistant/components/swiss_*/* @fabaff
|
||||
homeassistant/components/switchbot/switch.py @danielhiversen
|
||||
homeassistant/components/switchmate/switch.py @danielhiversen
|
||||
homeassistant/components/synology_srm/device_tracker.py @aerialls
|
||||
homeassistant/components/sytadin/sensor.py @gautric
|
||||
|
||||
# T
|
||||
homeassistant/components/tahoma/* @philklei
|
||||
homeassistant/components/tellduslive/*.py @fredrike
|
||||
homeassistant/components/tautulli/sensor.py @ludeeus
|
||||
homeassistant/components/tellduslive/* @fredrike
|
||||
homeassistant/components/template/cover.py @PhracturedBlue
|
||||
homeassistant/components/tesla/* @zabuldon
|
||||
homeassistant/components/tfiac/* @fredrike @mellado
|
||||
homeassistant/components/thethingsnetwork/* @fabaff
|
||||
homeassistant/components/threshold/binary_sensor.py @fabaff
|
||||
homeassistant/components/tibber/* @danielhiversen
|
||||
homeassistant/components/tplink/* @rytilahti
|
||||
homeassistant/components/tradfri/* @ggravlingen
|
||||
homeassistant/components/tile/device_tracker.py @bachya
|
||||
homeassistant/components/time_date/sensor.py @fabaff
|
||||
homeassistant/components/toon/* @frenck
|
||||
homeassistant/components/tplink/* @rytilahti
|
||||
homeassistant/components/traccar/device_tracker.py @ludeeus
|
||||
homeassistant/components/tradfri/* @ggravlingen
|
||||
|
||||
# U
|
||||
homeassistant/components/uber/sensor.py @robbiet480
|
||||
homeassistant/components/unifi/* @kane610
|
||||
homeassistant/components/upcloud/* @scop
|
||||
homeassistant/components/upnp/* @robbiet480
|
||||
homeassistant/components/uptimerobot/binary_sensor.py @ludeeus
|
||||
homeassistant/components/utility_meter/* @dgomes
|
||||
|
||||
# V
|
||||
homeassistant/components/velux/* @Julius2342
|
||||
homeassistant/components/version/sensor.py @fabaff
|
||||
|
||||
# W
|
||||
homeassistant/components/waqi/sensor.py @andrey-git
|
||||
homeassistant/components/weather/__init__.py @fabaff
|
||||
homeassistant/components/wemo/* @sqldiablo
|
||||
homeassistant/components/worldclock/sensor.py @fabaff
|
||||
|
||||
# X
|
||||
homeassistant/components/xfinity/device_tracker.py @cisasteelersfan
|
||||
homeassistant/components/xiaomi_aqara/* @danielhiversen @syssi
|
||||
homeassistant/components/xiaomi_miio/* @rytilahti @syssi
|
||||
homeassistant/components/xiaomi_tv/media_player.py @fattdev
|
||||
|
||||
# Y
|
||||
homeassistant/components/yamaha_musiccast/* @jalmeroth
|
||||
homeassistant/components/yeelight/* @rytilahti @zewelor
|
||||
homeassistant/components/yeelightsunflower/light.py @lindsaymarkward
|
||||
homeassistant/components/yi/camera.py @bachya
|
||||
|
||||
# Z
|
||||
homeassistant/components/zeroconf/* @robbiet480
|
||||
homeassistant/components/zha/* @dmulcahey @adminiuga
|
||||
homeassistant/components/zoneminder/* @rohankapoorcom
|
||||
|
||||
# Other code
|
||||
|
||||
@@ -18,8 +18,26 @@ from ..models import Credentials, UserMeta
|
||||
IPAddress = Union[IPv4Address, IPv6Address]
|
||||
IPNetwork = Union[IPv4Network, IPv6Network]
|
||||
|
||||
CONF_TRUSTED_NETWORKS = 'trusted_networks'
|
||||
CONF_TRUSTED_USERS = 'trusted_users'
|
||||
CONF_GROUP = 'group'
|
||||
CONF_ALLOW_BYPASS_LOGIN = 'allow_bypass_login'
|
||||
|
||||
CONFIG_SCHEMA = AUTH_PROVIDER_SCHEMA.extend({
|
||||
vol.Required('trusted_networks'): vol.All(cv.ensure_list, [ip_network])
|
||||
vol.Required(CONF_TRUSTED_NETWORKS): vol.All(
|
||||
cv.ensure_list, [ip_network]
|
||||
),
|
||||
vol.Optional(CONF_TRUSTED_USERS, default={}): vol.Schema(
|
||||
# we only validate the format of user_id or group_id
|
||||
{ip_network: vol.All(
|
||||
cv.ensure_list,
|
||||
[vol.Or(
|
||||
cv.uuid4_hex,
|
||||
vol.Schema({vol.Required(CONF_GROUP): cv.uuid4_hex}),
|
||||
)],
|
||||
)}
|
||||
),
|
||||
vol.Optional(CONF_ALLOW_BYPASS_LOGIN, default=False): cv.boolean,
|
||||
}, extra=vol.PREVENT_EXTRA)
|
||||
|
||||
|
||||
@@ -43,7 +61,12 @@ class TrustedNetworksAuthProvider(AuthProvider):
|
||||
@property
|
||||
def trusted_networks(self) -> List[IPNetwork]:
|
||||
"""Return trusted networks."""
|
||||
return cast(List[IPNetwork], self.config['trusted_networks'])
|
||||
return cast(List[IPNetwork], self.config[CONF_TRUSTED_NETWORKS])
|
||||
|
||||
@property
|
||||
def trusted_users(self) -> Dict[IPNetwork, Any]:
|
||||
"""Return trusted users per network."""
|
||||
return cast(Dict[IPNetwork, Any], self.config[CONF_TRUSTED_USERS])
|
||||
|
||||
@property
|
||||
def support_mfa(self) -> bool:
|
||||
@@ -53,13 +76,34 @@ class TrustedNetworksAuthProvider(AuthProvider):
|
||||
async def async_login_flow(self, context: Optional[Dict]) -> LoginFlow:
|
||||
"""Return a flow to login."""
|
||||
assert context is not None
|
||||
ip_addr = cast(IPAddress, context.get('ip_address'))
|
||||
users = await self.store.async_get_users()
|
||||
available_users = {user.id: user.name
|
||||
for user in users
|
||||
if not user.system_generated and user.is_active}
|
||||
available_users = [user for user in users
|
||||
if not user.system_generated and user.is_active]
|
||||
for ip_net, user_or_group_list in self.trusted_users.items():
|
||||
if ip_addr in ip_net:
|
||||
user_list = [user_id for user_id in user_or_group_list
|
||||
if isinstance(user_id, str)]
|
||||
group_list = [group[CONF_GROUP] for group in user_or_group_list
|
||||
if isinstance(group, dict)]
|
||||
flattened_group_list = [group for sublist in group_list
|
||||
for group in sublist]
|
||||
available_users = [
|
||||
user for user in available_users
|
||||
if (user.id in user_list or
|
||||
any([group.id in flattened_group_list
|
||||
for group in user.groups]))
|
||||
]
|
||||
break
|
||||
|
||||
return TrustedNetworksLoginFlow(
|
||||
self, cast(IPAddress, context.get('ip_address')), available_users)
|
||||
self,
|
||||
ip_addr,
|
||||
{
|
||||
user.id: user.name for user in available_users
|
||||
},
|
||||
self.config[CONF_ALLOW_BYPASS_LOGIN],
|
||||
)
|
||||
|
||||
async def async_get_or_create_credentials(
|
||||
self, flow_result: Dict[str, str]) -> Credentials:
|
||||
@@ -109,11 +153,13 @@ class TrustedNetworksLoginFlow(LoginFlow):
|
||||
|
||||
def __init__(self, auth_provider: TrustedNetworksAuthProvider,
|
||||
ip_addr: IPAddress,
|
||||
available_users: Dict[str, Optional[str]]) -> None:
|
||||
available_users: Dict[str, Optional[str]],
|
||||
allow_bypass_login: bool) -> None:
|
||||
"""Initialize the login flow."""
|
||||
super().__init__(auth_provider)
|
||||
self._available_users = available_users
|
||||
self._ip_address = ip_addr
|
||||
self._allow_bypass_login = allow_bypass_login
|
||||
|
||||
async def async_step_init(
|
||||
self, user_input: Optional[Dict[str, str]] = None) \
|
||||
@@ -131,6 +177,11 @@ class TrustedNetworksLoginFlow(LoginFlow):
|
||||
if user_input is not None:
|
||||
return await self.async_finish(user_input)
|
||||
|
||||
if self._allow_bypass_login and len(self._available_users) == 1:
|
||||
return await self.async_finish({
|
||||
'user': next(iter(self._available_users.keys()))
|
||||
})
|
||||
|
||||
return self.async_show_form(
|
||||
step_id='init',
|
||||
data_schema=vol.Schema({'user': vol.In(self._available_users)}),
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
"""Provide methods to bootstrap a Home Assistant instance."""
|
||||
import asyncio
|
||||
import logging
|
||||
import logging.handlers
|
||||
import os
|
||||
@@ -9,10 +10,10 @@ from typing import Any, Optional, Dict, Set
|
||||
|
||||
import voluptuous as vol
|
||||
|
||||
from homeassistant import (
|
||||
core, config as conf_util, config_entries, components as core_components,
|
||||
loader)
|
||||
from homeassistant.components import persistent_notification
|
||||
from homeassistant import core, config as conf_util, config_entries, loader
|
||||
from homeassistant.components import (
|
||||
persistent_notification, homeassistant as core_component
|
||||
)
|
||||
from homeassistant.const import EVENT_HOMEASSISTANT_CLOSE
|
||||
from homeassistant.setup import async_setup_component
|
||||
from homeassistant.util.logging import AsyncHandler
|
||||
@@ -139,7 +140,7 @@ async def async_from_config_dict(config: Dict[str, Any],
|
||||
pass
|
||||
|
||||
# setup components
|
||||
res = await core_components.async_setup(hass, config)
|
||||
res = await core_component.async_setup(hass, config)
|
||||
if not res:
|
||||
_LOGGER.error("Home Assistant core failed to initialize. "
|
||||
"Further initialization aborted")
|
||||
@@ -157,6 +158,12 @@ async def async_from_config_dict(config: Dict[str, Any],
|
||||
|
||||
await hass.async_block_till_done()
|
||||
|
||||
# Kick off loading the registries. They don't need to be awaited.
|
||||
asyncio.gather(
|
||||
hass.helpers.device_registry.async_get_registry(),
|
||||
hass.helpers.entity_registry.async_get_registry(),
|
||||
hass.helpers.area_registry.async_get_registry())
|
||||
|
||||
# stage 1
|
||||
for component in components:
|
||||
if component in FIRST_INIT_COMPONENT:
|
||||
|
||||
@@ -7,33 +7,12 @@ Component design guidelines:
|
||||
format "<DOMAIN>.<OBJECT_ID>".
|
||||
- Each component should publish services only under its own domain.
|
||||
"""
|
||||
import asyncio
|
||||
import itertools as it
|
||||
import logging
|
||||
from typing import Awaitable
|
||||
|
||||
import voluptuous as vol
|
||||
|
||||
import homeassistant.core as ha
|
||||
import homeassistant.config as conf_util
|
||||
from homeassistant.exceptions import HomeAssistantError
|
||||
from homeassistant.helpers.service import async_extract_entity_ids
|
||||
from homeassistant.helpers import intent
|
||||
from homeassistant.const import (
|
||||
ATTR_ENTITY_ID, SERVICE_TURN_ON, SERVICE_TURN_OFF, SERVICE_TOGGLE,
|
||||
SERVICE_HOMEASSISTANT_STOP, SERVICE_HOMEASSISTANT_RESTART,
|
||||
RESTART_EXIT_CODE)
|
||||
from homeassistant.helpers import config_validation as cv
|
||||
from homeassistant.core import split_entity_id
|
||||
|
||||
_LOGGER = logging.getLogger(__name__)
|
||||
|
||||
SERVICE_RELOAD_CORE_CONFIG = 'reload_core_config'
|
||||
SERVICE_CHECK_CONFIG = 'check_config'
|
||||
SERVICE_UPDATE_ENTITY = 'update_entity'
|
||||
SCHEMA_UPDATE_ENTITY = vol.Schema({
|
||||
ATTR_ENTITY_ID: cv.entity_ids
|
||||
})
|
||||
|
||||
|
||||
def is_on(hass, entity_id=None):
|
||||
"""Load up the module to call the is_on method.
|
||||
@@ -46,7 +25,7 @@ def is_on(hass, entity_id=None):
|
||||
entity_ids = hass.states.entity_ids()
|
||||
|
||||
for ent_id in entity_ids:
|
||||
domain = ha.split_entity_id(ent_id)[0]
|
||||
domain = split_entity_id(ent_id)[0]
|
||||
|
||||
try:
|
||||
component = getattr(hass.components, domain)
|
||||
@@ -64,113 +43,3 @@ def is_on(hass, entity_id=None):
|
||||
return True
|
||||
|
||||
return False
|
||||
|
||||
|
||||
async def async_setup(hass: ha.HomeAssistant, config: dict) -> Awaitable[bool]:
|
||||
"""Set up general services related to Home Assistant."""
|
||||
async def async_handle_turn_service(service):
|
||||
"""Handle calls to homeassistant.turn_on/off."""
|
||||
entity_ids = await async_extract_entity_ids(hass, service)
|
||||
|
||||
# Generic turn on/off method requires entity id
|
||||
if not entity_ids:
|
||||
_LOGGER.error(
|
||||
"homeassistant/%s cannot be called without entity_id",
|
||||
service.service)
|
||||
return
|
||||
|
||||
# Group entity_ids by domain. groupby requires sorted data.
|
||||
by_domain = it.groupby(sorted(entity_ids),
|
||||
lambda item: ha.split_entity_id(item)[0])
|
||||
|
||||
tasks = []
|
||||
|
||||
for domain, ent_ids in by_domain:
|
||||
# We want to block for all calls and only return when all calls
|
||||
# have been processed. If a service does not exist it causes a 10
|
||||
# second delay while we're blocking waiting for a response.
|
||||
# But services can be registered on other HA instances that are
|
||||
# listening to the bus too. So as an in between solution, we'll
|
||||
# block only if the service is defined in the current HA instance.
|
||||
blocking = hass.services.has_service(domain, service.service)
|
||||
|
||||
# Create a new dict for this call
|
||||
data = dict(service.data)
|
||||
|
||||
# ent_ids is a generator, convert it to a list.
|
||||
data[ATTR_ENTITY_ID] = list(ent_ids)
|
||||
|
||||
tasks.append(hass.services.async_call(
|
||||
domain, service.service, data, blocking))
|
||||
|
||||
await asyncio.wait(tasks, loop=hass.loop)
|
||||
|
||||
hass.services.async_register(
|
||||
ha.DOMAIN, SERVICE_TURN_OFF, async_handle_turn_service)
|
||||
hass.services.async_register(
|
||||
ha.DOMAIN, SERVICE_TURN_ON, async_handle_turn_service)
|
||||
hass.services.async_register(
|
||||
ha.DOMAIN, SERVICE_TOGGLE, async_handle_turn_service)
|
||||
hass.helpers.intent.async_register(intent.ServiceIntentHandler(
|
||||
intent.INTENT_TURN_ON, ha.DOMAIN, SERVICE_TURN_ON, "Turned {} on"))
|
||||
hass.helpers.intent.async_register(intent.ServiceIntentHandler(
|
||||
intent.INTENT_TURN_OFF, ha.DOMAIN, SERVICE_TURN_OFF,
|
||||
"Turned {} off"))
|
||||
hass.helpers.intent.async_register(intent.ServiceIntentHandler(
|
||||
intent.INTENT_TOGGLE, ha.DOMAIN, SERVICE_TOGGLE, "Toggled {}"))
|
||||
|
||||
async def async_handle_core_service(call):
|
||||
"""Service handler for handling core services."""
|
||||
if call.service == SERVICE_HOMEASSISTANT_STOP:
|
||||
hass.async_create_task(hass.async_stop())
|
||||
return
|
||||
|
||||
try:
|
||||
errors = await conf_util.async_check_ha_config_file(hass)
|
||||
except HomeAssistantError:
|
||||
return
|
||||
|
||||
if errors:
|
||||
_LOGGER.error(errors)
|
||||
hass.components.persistent_notification.async_create(
|
||||
"Config error. See dev-info panel for details.",
|
||||
"Config validating", "{0}.check_config".format(ha.DOMAIN))
|
||||
return
|
||||
|
||||
if call.service == SERVICE_HOMEASSISTANT_RESTART:
|
||||
hass.async_create_task(hass.async_stop(RESTART_EXIT_CODE))
|
||||
|
||||
async def async_handle_update_service(call):
|
||||
"""Service handler for updating an entity."""
|
||||
tasks = [hass.helpers.entity_component.async_update_entity(entity)
|
||||
for entity in call.data[ATTR_ENTITY_ID]]
|
||||
|
||||
if tasks:
|
||||
await asyncio.wait(tasks)
|
||||
|
||||
hass.services.async_register(
|
||||
ha.DOMAIN, SERVICE_HOMEASSISTANT_STOP, async_handle_core_service)
|
||||
hass.services.async_register(
|
||||
ha.DOMAIN, SERVICE_HOMEASSISTANT_RESTART, async_handle_core_service)
|
||||
hass.services.async_register(
|
||||
ha.DOMAIN, SERVICE_CHECK_CONFIG, async_handle_core_service)
|
||||
hass.services.async_register(
|
||||
ha.DOMAIN, SERVICE_UPDATE_ENTITY, async_handle_update_service,
|
||||
schema=SCHEMA_UPDATE_ENTITY)
|
||||
|
||||
async def async_handle_reload_config(call):
|
||||
"""Service handler for reloading core config."""
|
||||
try:
|
||||
conf = await conf_util.async_hass_config_yaml(hass)
|
||||
except HomeAssistantError as err:
|
||||
_LOGGER.error(err)
|
||||
return
|
||||
|
||||
# auth only processed during startup
|
||||
await conf_util.async_process_ha_core_config(
|
||||
hass, conf.get(ha.DOMAIN) or {})
|
||||
|
||||
hass.services.async_register(
|
||||
ha.DOMAIN, SERVICE_RELOAD_CORE_CONFIG, async_handle_reload_config)
|
||||
|
||||
return True
|
||||
|
||||
@@ -2,12 +2,12 @@
|
||||
import logging
|
||||
|
||||
import homeassistant.components.alarm_control_panel as alarm
|
||||
from homeassistant.components.abode import ATTRIBUTION, AbodeDevice
|
||||
from homeassistant.components.abode import DOMAIN as ABODE_DOMAIN
|
||||
from homeassistant.const import (
|
||||
ATTR_ATTRIBUTION, STATE_ALARM_ARMED_AWAY, STATE_ALARM_ARMED_HOME,
|
||||
STATE_ALARM_DISARMED)
|
||||
|
||||
from . import ATTRIBUTION, DOMAIN as ABODE_DOMAIN, AbodeDevice
|
||||
|
||||
DEPENDENCIES = ['abode']
|
||||
|
||||
_LOGGER = logging.getLogger(__name__)
|
||||
|
||||
@@ -1,10 +1,10 @@
|
||||
"""Support for Abode Security System binary sensors."""
|
||||
import logging
|
||||
|
||||
from homeassistant.components.abode import (AbodeDevice, AbodeAutomation,
|
||||
DOMAIN as ABODE_DOMAIN)
|
||||
from homeassistant.components.binary_sensor import BinarySensorDevice
|
||||
|
||||
from . import DOMAIN as ABODE_DOMAIN, AbodeAutomation, AbodeDevice
|
||||
|
||||
_LOGGER = logging.getLogger(__name__)
|
||||
|
||||
DEPENDENCIES = ['abode']
|
||||
|
||||
@@ -1,13 +1,14 @@
|
||||
"""Support for Abode Security System cameras."""
|
||||
from datetime import timedelta
|
||||
import logging
|
||||
|
||||
from datetime import timedelta
|
||||
import requests
|
||||
|
||||
from homeassistant.components.abode import AbodeDevice, DOMAIN as ABODE_DOMAIN
|
||||
from homeassistant.components.camera import Camera
|
||||
from homeassistant.util import Throttle
|
||||
|
||||
from . import DOMAIN as ABODE_DOMAIN, AbodeDevice
|
||||
|
||||
DEPENDENCIES = ['abode']
|
||||
|
||||
MIN_TIME_BETWEEN_UPDATES = timedelta(seconds=90)
|
||||
|
||||
@@ -1,9 +1,10 @@
|
||||
"""Support for Abode Security System covers."""
|
||||
import logging
|
||||
|
||||
from homeassistant.components.abode import AbodeDevice, DOMAIN as ABODE_DOMAIN
|
||||
from homeassistant.components.cover import CoverDevice
|
||||
|
||||
from . import DOMAIN as ABODE_DOMAIN, AbodeDevice
|
||||
|
||||
DEPENDENCIES = ['abode']
|
||||
|
||||
_LOGGER = logging.getLogger(__name__)
|
||||
|
||||
@@ -1,13 +1,14 @@
|
||||
"""Support for Abode Security System lights."""
|
||||
import logging
|
||||
from math import ceil
|
||||
from homeassistant.components.abode import AbodeDevice, DOMAIN as ABODE_DOMAIN
|
||||
|
||||
from homeassistant.components.light import (
|
||||
ATTR_BRIGHTNESS, ATTR_HS_COLOR, ATTR_COLOR_TEMP,
|
||||
SUPPORT_BRIGHTNESS, SUPPORT_COLOR, SUPPORT_COLOR_TEMP, Light)
|
||||
ATTR_BRIGHTNESS, ATTR_COLOR_TEMP, ATTR_HS_COLOR, SUPPORT_BRIGHTNESS,
|
||||
SUPPORT_COLOR, SUPPORT_COLOR_TEMP, Light)
|
||||
from homeassistant.util.color import (
|
||||
color_temperature_kelvin_to_mired, color_temperature_mired_to_kelvin)
|
||||
|
||||
from . import DOMAIN as ABODE_DOMAIN, AbodeDevice
|
||||
|
||||
DEPENDENCIES = ['abode']
|
||||
|
||||
|
||||
@@ -1,9 +1,10 @@
|
||||
"""Support for Abode Security System locks."""
|
||||
import logging
|
||||
|
||||
from homeassistant.components.abode import AbodeDevice, DOMAIN as ABODE_DOMAIN
|
||||
from homeassistant.components.lock import LockDevice
|
||||
|
||||
from . import DOMAIN as ABODE_DOMAIN, AbodeDevice
|
||||
|
||||
DEPENDENCIES = ['abode']
|
||||
|
||||
_LOGGER = logging.getLogger(__name__)
|
||||
|
||||
@@ -1,10 +1,11 @@
|
||||
"""Support for Abode Security System sensors."""
|
||||
import logging
|
||||
|
||||
from homeassistant.components.abode import AbodeDevice, DOMAIN as ABODE_DOMAIN
|
||||
from homeassistant.const import (
|
||||
DEVICE_CLASS_HUMIDITY, DEVICE_CLASS_ILLUMINANCE, DEVICE_CLASS_TEMPERATURE)
|
||||
|
||||
from . import DOMAIN as ABODE_DOMAIN, AbodeDevice
|
||||
|
||||
_LOGGER = logging.getLogger(__name__)
|
||||
|
||||
DEPENDENCIES = ['abode']
|
||||
|
||||
@@ -1,10 +1,10 @@
|
||||
"""Support for Abode Security System switches."""
|
||||
import logging
|
||||
|
||||
from homeassistant.components.abode import (AbodeDevice, AbodeAutomation,
|
||||
DOMAIN as ABODE_DOMAIN)
|
||||
from homeassistant.components.switch import SwitchDevice
|
||||
|
||||
from . import DOMAIN as ABODE_DOMAIN, AbodeAutomation, AbodeDevice
|
||||
|
||||
_LOGGER = logging.getLogger(__name__)
|
||||
|
||||
DEPENDENCIES = ['abode']
|
||||
|
||||
1
homeassistant/components/acer_projector/__init__.py
Normal file
1
homeassistant/components/acer_projector/__init__.py
Normal file
@@ -0,0 +1 @@
|
||||
"""The acer_projector component."""
|
||||
1
homeassistant/components/actiontec/__init__.py
Normal file
1
homeassistant/components/actiontec/__init__.py
Normal file
@@ -0,0 +1 @@
|
||||
"""The actiontec component."""
|
||||
@@ -3,12 +3,13 @@ import logging
|
||||
|
||||
import voluptuous as vol
|
||||
|
||||
from homeassistant.components.ads import CONF_ADS_VAR, DATA_ADS
|
||||
from homeassistant.components.binary_sensor import (
|
||||
DEVICE_CLASSES_SCHEMA, PLATFORM_SCHEMA, BinarySensorDevice)
|
||||
from homeassistant.const import CONF_DEVICE_CLASS, CONF_NAME
|
||||
import homeassistant.helpers.config_validation as cv
|
||||
|
||||
from . import CONF_ADS_VAR, DATA_ADS
|
||||
|
||||
_LOGGER = logging.getLogger(__name__)
|
||||
|
||||
DEFAULT_NAME = 'ADS binary sensor'
|
||||
|
||||
@@ -1,13 +1,15 @@
|
||||
"""Support for ADS light sources."""
|
||||
import logging
|
||||
|
||||
import voluptuous as vol
|
||||
from homeassistant.components.light import Light, ATTR_BRIGHTNESS, \
|
||||
SUPPORT_BRIGHTNESS, PLATFORM_SCHEMA
|
||||
|
||||
from homeassistant.components.light import (
|
||||
ATTR_BRIGHTNESS, PLATFORM_SCHEMA, SUPPORT_BRIGHTNESS, Light)
|
||||
from homeassistant.const import CONF_NAME
|
||||
from homeassistant.components.ads import DATA_ADS, CONF_ADS_VAR, \
|
||||
CONF_ADS_VAR_BRIGHTNESS
|
||||
import homeassistant.helpers.config_validation as cv
|
||||
|
||||
from . import CONF_ADS_VAR, CONF_ADS_VAR_BRIGHTNESS, DATA_ADS
|
||||
|
||||
_LOGGER = logging.getLogger(__name__)
|
||||
DEPENDENCIES = ['ads']
|
||||
DEFAULT_NAME = 'ADS Light'
|
||||
|
||||
@@ -4,13 +4,13 @@ import logging
|
||||
import voluptuous as vol
|
||||
|
||||
from homeassistant.components import ads
|
||||
from homeassistant.components.ads import (
|
||||
CONF_ADS_FACTOR, CONF_ADS_TYPE, CONF_ADS_VAR)
|
||||
from homeassistant.components.sensor import PLATFORM_SCHEMA
|
||||
from homeassistant.const import CONF_NAME, CONF_UNIT_OF_MEASUREMENT
|
||||
import homeassistant.helpers.config_validation as cv
|
||||
from homeassistant.helpers.entity import Entity
|
||||
|
||||
from . import CONF_ADS_FACTOR, CONF_ADS_TYPE, CONF_ADS_VAR
|
||||
|
||||
_LOGGER = logging.getLogger(__name__)
|
||||
|
||||
DEFAULT_NAME = "ADS sensor"
|
||||
|
||||
@@ -3,12 +3,13 @@ import logging
|
||||
|
||||
import voluptuous as vol
|
||||
|
||||
from homeassistant.components.ads import CONF_ADS_VAR, DATA_ADS
|
||||
from homeassistant.components.switch import PLATFORM_SCHEMA
|
||||
from homeassistant.const import CONF_NAME
|
||||
import homeassistant.helpers.config_validation as cv
|
||||
from homeassistant.helpers.entity import ToggleEntity
|
||||
|
||||
from . import CONF_ADS_VAR, DATA_ADS
|
||||
|
||||
_LOGGER = logging.getLogger(__name__)
|
||||
|
||||
DEPENDENCIES = ['ads']
|
||||
|
||||
1
homeassistant/components/aftership/__init__.py
Normal file
1
homeassistant/components/aftership/__init__.py
Normal file
@@ -0,0 +1 @@
|
||||
"""The aftership component."""
|
||||
1
homeassistant/components/airvisual/__init__.py
Normal file
1
homeassistant/components/airvisual/__init__.py
Normal file
@@ -0,0 +1 @@
|
||||
"""The airvisual component."""
|
||||
1
homeassistant/components/aladdin_connect/__init__.py
Normal file
1
homeassistant/components/aladdin_connect/__init__.py
Normal file
@@ -0,0 +1 @@
|
||||
"""The aladdin_connect component."""
|
||||
@@ -5,7 +5,7 @@ from datetime import timedelta
|
||||
import voluptuous as vol
|
||||
|
||||
import homeassistant.helpers.config_validation as cv
|
||||
from homeassistant.const import EVENT_HOMEASSISTANT_STOP
|
||||
from homeassistant.const import EVENT_HOMEASSISTANT_STOP, CONF_HOST
|
||||
from homeassistant.helpers.discovery import load_platform
|
||||
from homeassistant.util import dt as dt_util
|
||||
from homeassistant.components.binary_sensor import DEVICE_CLASSES_SCHEMA
|
||||
@@ -20,7 +20,6 @@ DATA_AD = 'alarmdecoder'
|
||||
|
||||
CONF_DEVICE = 'device'
|
||||
CONF_DEVICE_BAUD = 'baudrate'
|
||||
CONF_DEVICE_HOST = 'host'
|
||||
CONF_DEVICE_PATH = 'path'
|
||||
CONF_DEVICE_PORT = 'port'
|
||||
CONF_DEVICE_TYPE = 'type'
|
||||
@@ -55,7 +54,7 @@ SIGNAL_REL_MESSAGE = 'alarmdecoder.rel_message'
|
||||
|
||||
DEVICE_SOCKET_SCHEMA = vol.Schema({
|
||||
vol.Required(CONF_DEVICE_TYPE): 'socket',
|
||||
vol.Optional(CONF_DEVICE_HOST, default=DEFAULT_DEVICE_HOST): cv.string,
|
||||
vol.Optional(CONF_HOST, default=DEFAULT_DEVICE_HOST): cv.string,
|
||||
vol.Optional(CONF_DEVICE_PORT, default=DEFAULT_DEVICE_PORT): cv.port})
|
||||
|
||||
DEVICE_SERIAL_SCHEMA = vol.Schema({
|
||||
@@ -165,7 +164,7 @@ def setup(hass, config):
|
||||
|
||||
controller = False
|
||||
if device_type == 'socket':
|
||||
host = device.get(CONF_DEVICE_HOST)
|
||||
host = device.get(CONF_HOST)
|
||||
port = device.get(CONF_DEVICE_PORT)
|
||||
controller = AlarmDecoder(SocketDevice(interface=(host, port)))
|
||||
elif device_type == 'serial':
|
||||
|
||||
@@ -4,12 +4,13 @@ import logging
|
||||
import voluptuous as vol
|
||||
|
||||
import homeassistant.components.alarm_control_panel as alarm
|
||||
from homeassistant.components.alarmdecoder import DATA_AD, SIGNAL_PANEL_MESSAGE
|
||||
from homeassistant.const import (
|
||||
ATTR_CODE, STATE_ALARM_ARMED_AWAY, STATE_ALARM_ARMED_HOME,
|
||||
STATE_ALARM_DISARMED, STATE_ALARM_TRIGGERED)
|
||||
import homeassistant.helpers.config_validation as cv
|
||||
|
||||
from . import DATA_AD, SIGNAL_PANEL_MESSAGE
|
||||
|
||||
_LOGGER = logging.getLogger(__name__)
|
||||
|
||||
DEPENDENCIES = ['alarmdecoder']
|
||||
|
||||
@@ -2,11 +2,11 @@
|
||||
import logging
|
||||
|
||||
from homeassistant.components.binary_sensor import BinarySensorDevice
|
||||
from homeassistant.components.alarmdecoder import (
|
||||
ZONE_SCHEMA, CONF_ZONES, CONF_ZONE_NAME, CONF_ZONE_TYPE,
|
||||
CONF_ZONE_RFID, CONF_ZONE_LOOP, SIGNAL_ZONE_FAULT, SIGNAL_ZONE_RESTORE,
|
||||
SIGNAL_RFX_MESSAGE, SIGNAL_REL_MESSAGE, CONF_RELAY_ADDR,
|
||||
CONF_RELAY_CHAN)
|
||||
|
||||
from . import (
|
||||
CONF_RELAY_ADDR, CONF_RELAY_CHAN, CONF_ZONE_LOOP, CONF_ZONE_NAME,
|
||||
CONF_ZONE_RFID, CONF_ZONE_TYPE, CONF_ZONES, SIGNAL_REL_MESSAGE,
|
||||
SIGNAL_RFX_MESSAGE, SIGNAL_ZONE_FAULT, SIGNAL_ZONE_RESTORE, ZONE_SCHEMA)
|
||||
|
||||
DEPENDENCIES = ['alarmdecoder']
|
||||
|
||||
|
||||
@@ -2,7 +2,8 @@
|
||||
import logging
|
||||
|
||||
from homeassistant.helpers.entity import Entity
|
||||
from homeassistant.components.alarmdecoder import (SIGNAL_PANEL_MESSAGE)
|
||||
|
||||
from . import SIGNAL_PANEL_MESSAGE
|
||||
|
||||
_LOGGER = logging.getLogger(__name__)
|
||||
|
||||
|
||||
1
homeassistant/components/alarmdotcom/__init__.py
Normal file
1
homeassistant/components/alarmdotcom/__init__.py
Normal file
@@ -0,0 +1 @@
|
||||
"""The alarmdotcom component."""
|
||||
@@ -65,6 +65,12 @@ API_THERMOSTAT_MODES = OrderedDict([
|
||||
(climate.STATE_DRY, 'OFF'),
|
||||
])
|
||||
|
||||
PERCENTAGE_FAN_MAP = {
|
||||
fan.SPEED_LOW: 33,
|
||||
fan.SPEED_MEDIUM: 66,
|
||||
fan.SPEED_HIGH: 100,
|
||||
}
|
||||
|
||||
SMART_HOME_HTTP_ENDPOINT = '/api/alexa/smart_home'
|
||||
|
||||
CONF_DESCRIPTION = 'description'
|
||||
@@ -580,6 +586,26 @@ class _AlexaPercentageController(_AlexaInterface):
|
||||
def name(self):
|
||||
return 'Alexa.PercentageController'
|
||||
|
||||
def properties_supported(self):
|
||||
return [{'name': 'percentage'}]
|
||||
|
||||
def properties_retrievable(self):
|
||||
return True
|
||||
|
||||
def get_property(self, name):
|
||||
if name != 'percentage':
|
||||
raise _UnsupportedProperty(name)
|
||||
|
||||
if self.entity.domain == fan.DOMAIN:
|
||||
speed = self.entity.attributes.get(fan.ATTR_SPEED)
|
||||
|
||||
return PERCENTAGE_FAN_MAP.get(speed, 0)
|
||||
|
||||
if self.entity.domain == cover.DOMAIN:
|
||||
return self.entity.attributes.get(cover.ATTR_CURRENT_POSITION, 0)
|
||||
|
||||
return 0
|
||||
|
||||
|
||||
class _AlexaSpeaker(_AlexaInterface):
|
||||
"""Implements Alexa.Speaker.
|
||||
|
||||
1
homeassistant/components/alpha_vantage/__init__.py
Normal file
1
homeassistant/components/alpha_vantage/__init__.py
Normal file
@@ -0,0 +1 @@
|
||||
"""The alpha_vantage component."""
|
||||
@@ -8,9 +8,10 @@ import logging
|
||||
|
||||
import voluptuous as vol
|
||||
|
||||
from homeassistant.components.tts import Provider, PLATFORM_SCHEMA
|
||||
import homeassistant.helpers.config_validation as cv
|
||||
|
||||
from . import PLATFORM_SCHEMA, Provider
|
||||
|
||||
REQUIREMENTS = ['boto3==1.9.16']
|
||||
|
||||
_LOGGER = logging.getLogger(__name__)
|
||||
@@ -0,0 +1,19 @@
|
||||
{
|
||||
"config": {
|
||||
"error": {
|
||||
"identifier_exists": "Application \u0438/\u0438\u043b\u0438 API \u043a\u043b\u044e\u0447\u044a\u0442 \u0432\u0435\u0447\u0435 \u0441\u0430 \u0440\u0435\u0433\u0438\u0441\u0442\u0440\u0438\u0440\u0430\u043d\u0438",
|
||||
"invalid_key": "\u041d\u0435\u0432\u0430\u043b\u0438\u0434\u0435\u043d API \u043a\u043b\u044e\u0447 \u0438/\u0438\u043b\u0438 Application \u043a\u043b\u044e\u0447",
|
||||
"no_devices": "\u041d\u0435 \u0441\u0430 \u043d\u0430\u043c\u0435\u0440\u0435\u043d\u0438 \u0443\u0441\u0442\u0440\u043e\u0439\u0441\u0442\u0432\u0430 \u0432 \u043f\u0440\u043e\u0444\u0438\u043b\u0430"
|
||||
},
|
||||
"step": {
|
||||
"user": {
|
||||
"data": {
|
||||
"api_key": "API \u043a\u043b\u044e\u0447",
|
||||
"app_key": "Application \u043a\u043b\u044e\u0447"
|
||||
},
|
||||
"title": "\u041f\u043e\u043f\u044a\u043b\u043d\u0435\u0442\u0435 \u0438\u043d\u0444\u043e\u0440\u043c\u0430\u0446\u0438\u044f\u0442\u0430 \u0441\u0438"
|
||||
}
|
||||
},
|
||||
"title": "\u0410\u0442\u043c\u043e\u0441\u0444\u0435\u0440\u043d\u0430 PWS"
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,19 @@
|
||||
{
|
||||
"config": {
|
||||
"error": {
|
||||
"identifier_exists": "Application Key \u548c/\u6216 API Key \u5df2\u6ce8\u518c",
|
||||
"invalid_key": "\u65e0\u6548\u7684 API \u5bc6\u94a5\u548c/\u6216 Application Key",
|
||||
"no_devices": "\u6ca1\u6709\u5728\u5e10\u6237\u4e2d\u627e\u5230\u8bbe\u5907"
|
||||
},
|
||||
"step": {
|
||||
"user": {
|
||||
"data": {
|
||||
"api_key": "API Key",
|
||||
"app_key": "Application Key"
|
||||
},
|
||||
"title": "\u586b\u5199\u60a8\u7684\u4fe1\u606f"
|
||||
}
|
||||
},
|
||||
"title": "Ambient PWS"
|
||||
}
|
||||
}
|
||||
@@ -304,15 +304,26 @@ class AmbientStation:
|
||||
self.monitored_conditions = monitored_conditions
|
||||
self.stations = {}
|
||||
|
||||
async def ws_connect(self):
|
||||
"""Register handlers and connect to the websocket."""
|
||||
async def _attempt_connect(self):
|
||||
"""Attempt to connect to the socket (retrying later on fail)."""
|
||||
from aioambient.errors import WebsocketError
|
||||
|
||||
try:
|
||||
await self.client.websocket.connect()
|
||||
except WebsocketError as err:
|
||||
_LOGGER.error("Error with the websocket connection: %s", err)
|
||||
self._ws_reconnect_delay = min(
|
||||
2 * self._ws_reconnect_delay, 480)
|
||||
async_call_later(
|
||||
self._hass, self._ws_reconnect_delay, self.ws_connect)
|
||||
|
||||
async def ws_connect(self):
|
||||
"""Register handlers and connect to the websocket."""
|
||||
async def _ws_reconnect(event_time):
|
||||
"""Forcibly disconnect from and reconnect to the websocket."""
|
||||
_LOGGER.debug('Watchdog expired; forcing socket reconnection')
|
||||
await self.client.websocket.disconnect()
|
||||
await self.client.websocket.connect()
|
||||
await self._attempt_connect()
|
||||
|
||||
def on_connect():
|
||||
"""Define a handler to fire when the websocket is connected."""
|
||||
@@ -381,15 +392,7 @@ class AmbientStation:
|
||||
self.client.websocket.on_disconnect(on_disconnect)
|
||||
self.client.websocket.on_subscribed(on_subscribed)
|
||||
|
||||
try:
|
||||
await self.client.websocket.connect()
|
||||
except WebsocketError as err:
|
||||
_LOGGER.error("Error with the websocket connection: %s", err)
|
||||
|
||||
self._ws_reconnect_delay = min(2 * self._ws_reconnect_delay, 480)
|
||||
|
||||
async_call_later(
|
||||
self._hass, self._ws_reconnect_delay, self.ws_connect)
|
||||
await self._attempt_connect()
|
||||
|
||||
async def ws_disconnect(self):
|
||||
"""Disconnect from the websocket."""
|
||||
@@ -411,6 +414,13 @@ class AmbientWeatherEntity(Entity):
|
||||
self._state = None
|
||||
self._station_name = station_name
|
||||
|
||||
@property
|
||||
def available(self):
|
||||
"""Return True if entity is available."""
|
||||
return bool(
|
||||
self._ambient.stations[self._mac_address][ATTR_LAST_DATA].get(
|
||||
self._sensor_type))
|
||||
|
||||
@property
|
||||
def device_info(self):
|
||||
"""Return device registry information for this entity."""
|
||||
|
||||
@@ -1,13 +1,13 @@
|
||||
"""Support for Ambient Weather Station binary sensors."""
|
||||
import logging
|
||||
|
||||
from homeassistant.components.ambient_station import (
|
||||
SENSOR_TYPES, TYPE_BATT1, TYPE_BATT10, TYPE_BATT2, TYPE_BATT3, TYPE_BATT4,
|
||||
TYPE_BATT5, TYPE_BATT6, TYPE_BATT7, TYPE_BATT8, TYPE_BATT9, TYPE_BATTOUT,
|
||||
AmbientWeatherEntity)
|
||||
from homeassistant.components.binary_sensor import BinarySensorDevice
|
||||
from homeassistant.const import ATTR_NAME
|
||||
|
||||
from . import (
|
||||
SENSOR_TYPES, TYPE_BATT1, TYPE_BATT2, TYPE_BATT3, TYPE_BATT4, TYPE_BATT5,
|
||||
TYPE_BATT6, TYPE_BATT7, TYPE_BATT8, TYPE_BATT9, TYPE_BATT10, TYPE_BATTOUT,
|
||||
AmbientWeatherEntity)
|
||||
from .const import ATTR_LAST_DATA, DATA_CLIENT, DOMAIN, TYPE_BINARY_SENSOR
|
||||
|
||||
_LOGGER = logging.getLogger(__name__)
|
||||
|
||||
@@ -1,10 +1,9 @@
|
||||
"""Support for Ambient Weather Station sensors."""
|
||||
import logging
|
||||
|
||||
from homeassistant.components.ambient_station import (
|
||||
SENSOR_TYPES, AmbientWeatherEntity)
|
||||
from homeassistant.const import ATTR_NAME
|
||||
|
||||
from . import SENSOR_TYPES, AmbientWeatherEntity
|
||||
from .const import ATTR_LAST_DATA, DATA_CLIENT, DOMAIN, TYPE_SENSOR
|
||||
|
||||
_LOGGER = logging.getLogger(__name__)
|
||||
|
||||
@@ -4,8 +4,6 @@ from datetime import timedelta
|
||||
|
||||
import aiohttp
|
||||
import voluptuous as vol
|
||||
from requests.exceptions import HTTPError, ConnectTimeout
|
||||
from requests.exceptions import ConnectionError as ConnectError
|
||||
|
||||
from homeassistant.const import (
|
||||
CONF_NAME, CONF_HOST, CONF_PORT, CONF_USERNAME, CONF_PASSWORD,
|
||||
@@ -13,7 +11,8 @@ from homeassistant.const import (
|
||||
from homeassistant.helpers import discovery
|
||||
import homeassistant.helpers.config_validation as cv
|
||||
|
||||
REQUIREMENTS = ['amcrest==1.2.5']
|
||||
|
||||
REQUIREMENTS = ['amcrest==1.2.7']
|
||||
DEPENDENCIES = ['ffmpeg']
|
||||
|
||||
_LOGGER = logging.getLogger(__name__)
|
||||
@@ -91,7 +90,7 @@ CONFIG_SCHEMA = vol.Schema({
|
||||
|
||||
def setup(hass, config):
|
||||
"""Set up the Amcrest IP Camera component."""
|
||||
from amcrest import AmcrestCamera
|
||||
from amcrest import AmcrestCamera, AmcrestError
|
||||
|
||||
hass.data[DATA_AMCREST] = {}
|
||||
amcrest_cams = config[DOMAIN]
|
||||
@@ -105,7 +104,7 @@ def setup(hass, config):
|
||||
# pylint: disable=pointless-statement
|
||||
camera.current_time
|
||||
|
||||
except (ConnectError, ConnectTimeout, HTTPError) as ex:
|
||||
except AmcrestError as ex:
|
||||
_LOGGER.error("Unable to connect to Amcrest camera: %s", str(ex))
|
||||
hass.components.persistent_notification.create(
|
||||
'Error: {}<br />'
|
||||
|
||||
@@ -2,17 +2,14 @@
|
||||
import asyncio
|
||||
import logging
|
||||
|
||||
from requests import RequestException
|
||||
from urllib3.exceptions import ReadTimeoutError
|
||||
|
||||
from homeassistant.components.amcrest import (
|
||||
DATA_AMCREST, STREAM_SOURCE_LIST, TIMEOUT)
|
||||
from homeassistant.components.camera import Camera
|
||||
from homeassistant.components.ffmpeg import DATA_FFMPEG
|
||||
from homeassistant.const import CONF_NAME
|
||||
from homeassistant.helpers.aiohttp_client import (
|
||||
async_get_clientsession, async_aiohttp_proxy_web,
|
||||
async_aiohttp_proxy_stream)
|
||||
async_aiohttp_proxy_stream, async_aiohttp_proxy_web,
|
||||
async_get_clientsession)
|
||||
|
||||
from . import DATA_AMCREST, STREAM_SOURCE_LIST, TIMEOUT
|
||||
|
||||
DEPENDENCIES = ['amcrest', 'ffmpeg']
|
||||
|
||||
@@ -51,13 +48,15 @@ class AmcrestCam(Camera):
|
||||
|
||||
async def async_camera_image(self):
|
||||
"""Return a still image response from the camera."""
|
||||
from amcrest import AmcrestError
|
||||
|
||||
async with self._snapshot_lock:
|
||||
try:
|
||||
# Send the request to snap a picture and return raw jpg data
|
||||
response = await self.hass.async_add_executor_job(
|
||||
self._camera.snapshot, self._resolution)
|
||||
return response.data
|
||||
except (RequestException, ReadTimeoutError, ValueError) as error:
|
||||
except AmcrestError as error:
|
||||
_LOGGER.error(
|
||||
'Could not get camera image due to error %s', error)
|
||||
return None
|
||||
@@ -79,7 +78,7 @@ class AmcrestCam(Camera):
|
||||
self.hass, request, stream_coro)
|
||||
|
||||
# streaming via ffmpeg
|
||||
from haffmpeg import CameraMjpeg
|
||||
from haffmpeg.camera import CameraMjpeg
|
||||
|
||||
streaming_url = self._camera.rtsp_url(typeno=self._resolution)
|
||||
stream = CameraMjpeg(self._ffmpeg.binary, loop=self.hass.loop)
|
||||
@@ -87,8 +86,9 @@ class AmcrestCam(Camera):
|
||||
streaming_url, extra_cmd=self._ffmpeg_arguments)
|
||||
|
||||
try:
|
||||
stream_reader = await stream.get_reader()
|
||||
return await async_aiohttp_proxy_stream(
|
||||
self.hass, request, stream,
|
||||
self.hass, request, stream_reader,
|
||||
self._ffmpeg.ffmpeg_stream_content_type)
|
||||
finally:
|
||||
await stream.close()
|
||||
|
||||
@@ -2,9 +2,10 @@
|
||||
from datetime import timedelta
|
||||
import logging
|
||||
|
||||
from homeassistant.components.amcrest import DATA_AMCREST, SENSORS
|
||||
from homeassistant.helpers.entity import Entity
|
||||
from homeassistant.const import CONF_NAME, CONF_SENSORS
|
||||
from homeassistant.helpers.entity import Entity
|
||||
|
||||
from . import DATA_AMCREST, SENSORS
|
||||
|
||||
DEPENDENCIES = ['amcrest']
|
||||
|
||||
|
||||
@@ -1,11 +1,11 @@
|
||||
"""Support for toggling Amcrest IP camera settings."""
|
||||
import logging
|
||||
|
||||
from homeassistant.components.amcrest import DATA_AMCREST, SWITCHES
|
||||
from homeassistant.const import (
|
||||
CONF_NAME, CONF_SWITCHES, STATE_OFF, STATE_ON)
|
||||
from homeassistant.const import CONF_NAME, CONF_SWITCHES, STATE_OFF, STATE_ON
|
||||
from homeassistant.helpers.entity import ToggleEntity
|
||||
|
||||
from . import DATA_AMCREST, SWITCHES
|
||||
|
||||
_LOGGER = logging.getLogger(__name__)
|
||||
|
||||
DEPENDENCIES = ['amcrest']
|
||||
|
||||
@@ -18,7 +18,7 @@ from homeassistant.helpers.dispatcher import (
|
||||
from homeassistant.helpers.entity import Entity
|
||||
from homeassistant.helpers.event import async_track_point_in_utc_time
|
||||
from homeassistant.util.dt import utcnow
|
||||
from homeassistant.components.camera.mjpeg import (
|
||||
from homeassistant.components.mjpeg.camera import (
|
||||
CONF_MJPEG_URL, CONF_STILL_IMAGE_URL)
|
||||
|
||||
REQUIREMENTS = ['pydroid-ipcam==0.8']
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
"""Support for Android IP Webcam binary sensors."""
|
||||
from homeassistant.components.binary_sensor import BinarySensorDevice
|
||||
from homeassistant.components.android_ip_webcam import (
|
||||
KEY_MAP, DATA_IP_WEBCAM, AndroidIPCamEntity, CONF_HOST, CONF_NAME)
|
||||
|
||||
from . import CONF_HOST, CONF_NAME, DATA_IP_WEBCAM, KEY_MAP, AndroidIPCamEntity
|
||||
|
||||
DEPENDENCIES = ['android_ip_webcam']
|
||||
|
||||
|
||||
@@ -1,9 +1,10 @@
|
||||
"""Support for Android IP Webcam sensors."""
|
||||
from homeassistant.components.android_ip_webcam import (
|
||||
KEY_MAP, ICON_MAP, DATA_IP_WEBCAM, AndroidIPCamEntity, CONF_HOST,
|
||||
CONF_NAME, CONF_SENSORS)
|
||||
from homeassistant.helpers.icon import icon_for_battery_level
|
||||
|
||||
from . import (
|
||||
CONF_HOST, CONF_NAME, CONF_SENSORS, DATA_IP_WEBCAM, ICON_MAP, KEY_MAP,
|
||||
AndroidIPCamEntity)
|
||||
|
||||
DEPENDENCIES = ['android_ip_webcam']
|
||||
|
||||
|
||||
|
||||
@@ -1,8 +1,9 @@
|
||||
"""Support for Android IP Webcam settings."""
|
||||
from homeassistant.components.switch import SwitchDevice
|
||||
from homeassistant.components.android_ip_webcam import (
|
||||
KEY_MAP, ICON_MAP, DATA_IP_WEBCAM, AndroidIPCamEntity, CONF_HOST,
|
||||
CONF_NAME, CONF_SWITCHES)
|
||||
|
||||
from . import (
|
||||
CONF_HOST, CONF_NAME, CONF_SWITCHES, DATA_IP_WEBCAM, ICON_MAP, KEY_MAP,
|
||||
AndroidIPCamEntity)
|
||||
|
||||
DEPENDENCIES = ['android_ip_webcam']
|
||||
|
||||
|
||||
@@ -22,7 +22,7 @@ import homeassistant.helpers.config_validation as cv
|
||||
|
||||
ANDROIDTV_DOMAIN = 'androidtv'
|
||||
|
||||
REQUIREMENTS = ['androidtv==0.0.12']
|
||||
REQUIREMENTS = ['androidtv==0.0.14']
|
||||
|
||||
_LOGGER = logging.getLogger(__name__)
|
||||
|
||||
@@ -40,6 +40,8 @@ CONF_ADB_SERVER_IP = 'adb_server_ip'
|
||||
CONF_ADB_SERVER_PORT = 'adb_server_port'
|
||||
CONF_APPS = 'apps'
|
||||
CONF_GET_SOURCES = 'get_sources'
|
||||
CONF_TURN_ON_COMMAND = 'turn_on_command'
|
||||
CONF_TURN_OFF_COMMAND = 'turn_off_command'
|
||||
|
||||
DEFAULT_NAME = 'Android TV'
|
||||
DEFAULT_PORT = 5555
|
||||
@@ -59,27 +61,21 @@ SERVICE_ADB_COMMAND_SCHEMA = vol.Schema({
|
||||
})
|
||||
|
||||
|
||||
def has_adb_files(value):
|
||||
"""Check that ADB key files exist."""
|
||||
priv_key = value
|
||||
pub_key = '{}.pub'.format(value)
|
||||
cv.isfile(pub_key)
|
||||
return cv.isfile(priv_key)
|
||||
|
||||
|
||||
PLATFORM_SCHEMA = PLATFORM_SCHEMA.extend({
|
||||
vol.Required(CONF_HOST): cv.string,
|
||||
vol.Optional(CONF_DEVICE_CLASS, default=DEFAULT_DEVICE_CLASS):
|
||||
vol.In(DEVICE_CLASSES),
|
||||
vol.Optional(CONF_NAME, default=DEFAULT_NAME): cv.string,
|
||||
vol.Optional(CONF_PORT, default=DEFAULT_PORT): cv.port,
|
||||
vol.Optional(CONF_ADBKEY): has_adb_files,
|
||||
vol.Optional(CONF_ADBKEY): cv.isfile,
|
||||
vol.Optional(CONF_ADB_SERVER_IP): cv.string,
|
||||
vol.Optional(CONF_ADB_SERVER_PORT, default=DEFAULT_ADB_SERVER_PORT):
|
||||
cv.port,
|
||||
vol.Optional(CONF_GET_SOURCES, default=DEFAULT_GET_SOURCES): cv.boolean,
|
||||
vol.Optional(CONF_APPS, default=dict()):
|
||||
vol.Schema({cv.string: cv.string})
|
||||
vol.Schema({cv.string: cv.string}),
|
||||
vol.Optional(CONF_TURN_ON_COMMAND): cv.string,
|
||||
vol.Optional(CONF_TURN_OFF_COMMAND): cv.string
|
||||
})
|
||||
|
||||
# Translate from `AndroidTV` / `FireTV` reported state to HA state.
|
||||
@@ -136,12 +132,16 @@ def setup_platform(hass, config, add_entities, discovery_info=None):
|
||||
else:
|
||||
if aftv.DEVICE_CLASS == DEVICE_ANDROIDTV:
|
||||
device = AndroidTVDevice(aftv, config[CONF_NAME],
|
||||
config[CONF_APPS])
|
||||
config[CONF_APPS],
|
||||
config.get(CONF_TURN_ON_COMMAND),
|
||||
config.get(CONF_TURN_OFF_COMMAND))
|
||||
device_name = config[CONF_NAME] if CONF_NAME in config \
|
||||
else 'Android TV'
|
||||
else:
|
||||
device = FireTVDevice(aftv, config[CONF_NAME], config[CONF_APPS],
|
||||
config[CONF_GET_SOURCES])
|
||||
config[CONF_GET_SOURCES],
|
||||
config.get(CONF_TURN_ON_COMMAND),
|
||||
config.get(CONF_TURN_OFF_COMMAND))
|
||||
device_name = config[CONF_NAME] if CONF_NAME in config \
|
||||
else 'Fire TV'
|
||||
|
||||
@@ -163,7 +163,7 @@ def setup_platform(hass, config, add_entities, discovery_info=None):
|
||||
output = target_device.adb_command(cmd)
|
||||
|
||||
# log the output if there is any
|
||||
if output:
|
||||
if output and (not isinstance(output, str) or output.strip()):
|
||||
_LOGGER.info("Output of command '%s' from '%s': %s",
|
||||
cmd, target_device.entity_id, repr(output))
|
||||
|
||||
@@ -199,7 +199,8 @@ def adb_decorator(override_available=False):
|
||||
class ADBDevice(MediaPlayerDevice):
|
||||
"""Representation of an Android TV or Fire TV device."""
|
||||
|
||||
def __init__(self, aftv, name, apps):
|
||||
def __init__(self, aftv, name, apps, turn_on_command,
|
||||
turn_off_command):
|
||||
"""Initialize the Android TV / Fire TV device."""
|
||||
from androidtv.constants import APPS, KEYS
|
||||
|
||||
@@ -209,6 +210,9 @@ class ADBDevice(MediaPlayerDevice):
|
||||
self._apps.update(apps)
|
||||
self._keys = KEYS
|
||||
|
||||
self.turn_on_command = turn_on_command
|
||||
self.turn_off_command = turn_off_command
|
||||
|
||||
# ADB exceptions to catch
|
||||
if not self.aftv.adb_server_ip:
|
||||
# Using "python-adb" (Python ADB implementation)
|
||||
@@ -223,7 +227,7 @@ class ADBDevice(MediaPlayerDevice):
|
||||
TcpTimeoutException)
|
||||
else:
|
||||
# Using "pure-python-adb" (communicate with ADB server)
|
||||
self.exceptions = (ConnectionResetError,)
|
||||
self.exceptions = (ConnectionResetError, RuntimeError)
|
||||
|
||||
# Property attributes
|
||||
self._available = self.aftv.available
|
||||
@@ -278,12 +282,18 @@ class ADBDevice(MediaPlayerDevice):
|
||||
@adb_decorator()
|
||||
def turn_on(self):
|
||||
"""Turn on the device."""
|
||||
self.aftv.turn_on()
|
||||
if self.turn_on_command:
|
||||
self.aftv.adb_shell(self.turn_on_command)
|
||||
else:
|
||||
self.aftv.turn_on()
|
||||
|
||||
@adb_decorator()
|
||||
def turn_off(self):
|
||||
"""Turn off the device."""
|
||||
self.aftv.turn_off()
|
||||
if self.turn_off_command:
|
||||
self.aftv.adb_shell(self.turn_off_command)
|
||||
else:
|
||||
self.aftv.turn_off()
|
||||
|
||||
@adb_decorator()
|
||||
def media_previous_track(self):
|
||||
@@ -311,9 +321,11 @@ class ADBDevice(MediaPlayerDevice):
|
||||
class AndroidTVDevice(ADBDevice):
|
||||
"""Representation of an Android TV device."""
|
||||
|
||||
def __init__(self, aftv, name, apps):
|
||||
def __init__(self, aftv, name, apps, turn_on_command,
|
||||
turn_off_command):
|
||||
"""Initialize the Android TV device."""
|
||||
super().__init__(aftv, name, apps)
|
||||
super().__init__(aftv, name, apps, turn_on_command,
|
||||
turn_off_command)
|
||||
|
||||
self._device = None
|
||||
self._muted = None
|
||||
@@ -392,9 +404,11 @@ class AndroidTVDevice(ADBDevice):
|
||||
class FireTVDevice(ADBDevice):
|
||||
"""Representation of a Fire TV device."""
|
||||
|
||||
def __init__(self, aftv, name, apps, get_sources):
|
||||
def __init__(self, aftv, name, apps, get_sources,
|
||||
turn_on_command, turn_off_command):
|
||||
"""Initialize the Fire TV device."""
|
||||
super().__init__(aftv, name, apps)
|
||||
super().__init__(aftv, name, apps, turn_on_command,
|
||||
turn_off_command)
|
||||
|
||||
self._get_sources = get_sources
|
||||
self._running_apps = None
|
||||
|
||||
1
homeassistant/components/anel_pwrctrl/__init__.py
Normal file
1
homeassistant/components/anel_pwrctrl/__init__.py
Normal file
@@ -0,0 +1 @@
|
||||
"""The anel_pwrctrl component."""
|
||||
1
homeassistant/components/anthemav/__init__.py
Normal file
1
homeassistant/components/anthemav/__init__.py
Normal file
@@ -0,0 +1 @@
|
||||
"""The anthemav component."""
|
||||
1
homeassistant/components/api_streams/__init__.py
Normal file
1
homeassistant/components/api_streams/__init__.py
Normal file
@@ -0,0 +1 @@
|
||||
"""The api_streams component."""
|
||||
1
homeassistant/components/apns/__init__.py
Normal file
1
homeassistant/components/apns/__init__.py
Normal file
@@ -0,0 +1 @@
|
||||
"""The apns component."""
|
||||
@@ -9,13 +9,14 @@ import os
|
||||
|
||||
import voluptuous as vol
|
||||
|
||||
from homeassistant.helpers.event import track_state_change
|
||||
from homeassistant.config import load_yaml_config_file
|
||||
from homeassistant.components.notify import (
|
||||
ATTR_TARGET, ATTR_DATA, BaseNotificationService, DOMAIN, PLATFORM_SCHEMA)
|
||||
from homeassistant.const import CONF_NAME, CONF_PLATFORM, ATTR_NAME
|
||||
import homeassistant.helpers.config_validation as cv
|
||||
from homeassistant.const import ATTR_NAME, CONF_NAME, CONF_PLATFORM
|
||||
from homeassistant.helpers import template as template_helper
|
||||
import homeassistant.helpers.config_validation as cv
|
||||
from homeassistant.helpers.event import track_state_change
|
||||
|
||||
from homeassistant.components.notify import (
|
||||
ATTR_DATA, ATTR_TARGET, DOMAIN, PLATFORM_SCHEMA, BaseNotificationService)
|
||||
|
||||
REQUIREMENTS = ['apns2==0.3.0']
|
||||
|
||||
@@ -1,8 +1,6 @@
|
||||
"""Support for Apple TV media player."""
|
||||
import logging
|
||||
|
||||
from homeassistant.components.apple_tv import (
|
||||
ATTR_ATV, ATTR_POWER, DATA_APPLE_TV, DATA_ENTITIES)
|
||||
from homeassistant.components.media_player import MediaPlayerDevice
|
||||
from homeassistant.components.media_player.const import (
|
||||
MEDIA_TYPE_MUSIC, MEDIA_TYPE_TVSHOW, MEDIA_TYPE_VIDEO, SUPPORT_NEXT_TRACK,
|
||||
@@ -14,6 +12,8 @@ from homeassistant.const import (
|
||||
from homeassistant.core import callback
|
||||
import homeassistant.util.dt as dt_util
|
||||
|
||||
from . import ATTR_ATV, ATTR_POWER, DATA_APPLE_TV, DATA_ENTITIES
|
||||
|
||||
DEPENDENCIES = ['apple_tv']
|
||||
|
||||
_LOGGER = logging.getLogger(__name__)
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
"""Remote control support for Apple TV."""
|
||||
from homeassistant.components.apple_tv import (
|
||||
ATTR_ATV, ATTR_POWER, DATA_APPLE_TV)
|
||||
from homeassistant.components import remote
|
||||
from homeassistant.const import (CONF_NAME, CONF_HOST)
|
||||
from homeassistant.const import CONF_HOST, CONF_NAME
|
||||
|
||||
from . import ATTR_ATV, ATTR_POWER, DATA_APPLE_TV
|
||||
|
||||
DEPENDENCIES = ['apple_tv']
|
||||
|
||||
|
||||
@@ -4,12 +4,13 @@ import logging
|
||||
import voluptuous as vol
|
||||
|
||||
from homeassistant.components.sensor import PLATFORM_SCHEMA
|
||||
from homeassistant.const import (CONF_MONITORED_CONDITIONS,
|
||||
TEMP_CELSIUS, TEMP_FAHRENHEIT)
|
||||
from homeassistant.const import (
|
||||
CONF_MONITORED_CONDITIONS, TEMP_CELSIUS, TEMP_FAHRENHEIT)
|
||||
from homeassistant.core import callback
|
||||
from homeassistant.helpers.entity import Entity
|
||||
import homeassistant.components.aqualogic as aq
|
||||
import homeassistant.helpers.config_validation as cv
|
||||
from homeassistant.helpers.entity import Entity
|
||||
|
||||
from . import DOMAIN, UPDATE_TOPIC
|
||||
|
||||
_LOGGER = logging.getLogger(__name__)
|
||||
|
||||
@@ -46,7 +47,7 @@ async def async_setup_platform(
|
||||
"""Set up the sensor platform."""
|
||||
sensors = []
|
||||
|
||||
processor = hass.data[aq.DOMAIN]
|
||||
processor = hass.data[DOMAIN]
|
||||
for sensor_type in config.get(CONF_MONITORED_CONDITIONS):
|
||||
sensors.append(AquaLogicSensor(processor, sensor_type))
|
||||
|
||||
@@ -95,7 +96,7 @@ class AquaLogicSensor(Entity):
|
||||
async def async_added_to_hass(self):
|
||||
"""Register callbacks."""
|
||||
self.hass.helpers.dispatcher.async_dispatcher_connect(
|
||||
aq.UPDATE_TOPIC, self.async_update_callback)
|
||||
UPDATE_TOPIC, self.async_update_callback)
|
||||
|
||||
@callback
|
||||
def async_update_callback(self):
|
||||
|
||||
@@ -3,11 +3,12 @@ import logging
|
||||
|
||||
import voluptuous as vol
|
||||
|
||||
import homeassistant.helpers.config_validation as cv
|
||||
from homeassistant.components.switch import PLATFORM_SCHEMA, SwitchDevice
|
||||
from homeassistant.const import CONF_MONITORED_CONDITIONS
|
||||
from homeassistant.core import callback
|
||||
import homeassistant.components.aqualogic as aq
|
||||
from homeassistant.components.switch import SwitchDevice, PLATFORM_SCHEMA
|
||||
from homeassistant.const import (CONF_MONITORED_CONDITIONS)
|
||||
import homeassistant.helpers.config_validation as cv
|
||||
|
||||
from . import DOMAIN, UPDATE_TOPIC
|
||||
|
||||
DEPENDENCIES = ['aqualogic']
|
||||
|
||||
@@ -37,7 +38,7 @@ async def async_setup_platform(
|
||||
"""Set up the switch platform."""
|
||||
switches = []
|
||||
|
||||
processor = hass.data[aq.DOMAIN]
|
||||
processor = hass.data[DOMAIN]
|
||||
for switch_type in config.get(CONF_MONITORED_CONDITIONS):
|
||||
switches.append(AquaLogicSwitch(processor, switch_type))
|
||||
|
||||
@@ -101,7 +102,7 @@ class AquaLogicSwitch(SwitchDevice):
|
||||
async def async_added_to_hass(self):
|
||||
"""Register callbacks."""
|
||||
self.hass.helpers.dispatcher.async_dispatcher_connect(
|
||||
aq.UPDATE_TOPIC, self.async_update_callback)
|
||||
UPDATE_TOPIC, self.async_update_callback)
|
||||
|
||||
@callback
|
||||
def async_update_callback(self):
|
||||
|
||||
1
homeassistant/components/aquostv/__init__.py
Normal file
1
homeassistant/components/aquostv/__init__.py
Normal file
@@ -0,0 +1 @@
|
||||
"""The aquostv component."""
|
||||
1
homeassistant/components/arest/__init__.py
Normal file
1
homeassistant/components/arest/__init__.py
Normal file
@@ -0,0 +1 @@
|
||||
"""The arest component."""
|
||||
@@ -3,16 +3,16 @@ import logging
|
||||
|
||||
import voluptuous as vol
|
||||
|
||||
import homeassistant.helpers.config_validation as cv
|
||||
from homeassistant.core import callback
|
||||
from homeassistant.helpers.dispatcher import async_dispatcher_connect
|
||||
from homeassistant.components.alarm_control_panel import (
|
||||
AlarmControlPanel, PLATFORM_SCHEMA)
|
||||
from homeassistant.components.arlo import (
|
||||
DATA_ARLO, ATTRIBUTION, SIGNAL_UPDATE_ARLO)
|
||||
PLATFORM_SCHEMA, AlarmControlPanel)
|
||||
from homeassistant.const import (
|
||||
ATTR_ATTRIBUTION, STATE_ALARM_ARMED_AWAY, STATE_ALARM_ARMED_HOME,
|
||||
STATE_ALARM_DISARMED, STATE_ALARM_ARMED_NIGHT)
|
||||
STATE_ALARM_ARMED_NIGHT, STATE_ALARM_DISARMED)
|
||||
from homeassistant.core import callback
|
||||
import homeassistant.helpers.config_validation as cv
|
||||
from homeassistant.helpers.dispatcher import async_dispatcher_connect
|
||||
|
||||
from . import ATTRIBUTION, DATA_ARLO, SIGNAL_UPDATE_ARLO
|
||||
|
||||
_LOGGER = logging.getLogger(__name__)
|
||||
|
||||
|
||||
@@ -3,16 +3,16 @@ import logging
|
||||
|
||||
import voluptuous as vol
|
||||
|
||||
from homeassistant.core import callback
|
||||
import homeassistant.helpers.config_validation as cv
|
||||
from homeassistant.components.arlo import (
|
||||
DEFAULT_BRAND, DATA_ARLO, SIGNAL_UPDATE_ARLO)
|
||||
from homeassistant.components.camera import Camera, PLATFORM_SCHEMA
|
||||
from homeassistant.components.camera import PLATFORM_SCHEMA, Camera
|
||||
from homeassistant.components.ffmpeg import DATA_FFMPEG
|
||||
from homeassistant.const import ATTR_BATTERY_LEVEL
|
||||
from homeassistant.core import callback
|
||||
from homeassistant.helpers.aiohttp_client import async_aiohttp_proxy_stream
|
||||
import homeassistant.helpers.config_validation as cv
|
||||
from homeassistant.helpers.dispatcher import async_dispatcher_connect
|
||||
|
||||
from . import DATA_ARLO, DEFAULT_BRAND, SIGNAL_UPDATE_ARLO
|
||||
|
||||
_LOGGER = logging.getLogger(__name__)
|
||||
|
||||
ARLO_MODE_ARMED = 'armed'
|
||||
@@ -83,7 +83,7 @@ class ArloCam(Camera):
|
||||
|
||||
async def handle_async_mjpeg_stream(self, request):
|
||||
"""Generate an HTTP MJPEG stream from the camera."""
|
||||
from haffmpeg import CameraMjpeg
|
||||
from haffmpeg.camera import CameraMjpeg
|
||||
video = self._camera.last_video
|
||||
if not video:
|
||||
error_msg = \
|
||||
@@ -97,8 +97,9 @@ class ArloCam(Camera):
|
||||
video.video_url, extra_cmd=self._ffmpeg_arguments)
|
||||
|
||||
try:
|
||||
stream_reader = await stream.get_reader()
|
||||
return await async_aiohttp_proxy_stream(
|
||||
self.hass, request, stream,
|
||||
self.hass, request, stream_reader,
|
||||
self._ffmpeg.ffmpeg_stream_content_type)
|
||||
finally:
|
||||
await stream.close()
|
||||
|
||||
@@ -3,19 +3,18 @@ import logging
|
||||
|
||||
import voluptuous as vol
|
||||
|
||||
from homeassistant.core import callback
|
||||
import homeassistant.helpers.config_validation as cv
|
||||
from homeassistant.components.arlo import (
|
||||
ATTRIBUTION, DEFAULT_BRAND, DATA_ARLO, SIGNAL_UPDATE_ARLO)
|
||||
from homeassistant.components.sensor import PLATFORM_SCHEMA
|
||||
from homeassistant.const import (
|
||||
ATTR_ATTRIBUTION, CONF_MONITORED_CONDITIONS, TEMP_CELSIUS,
|
||||
DEVICE_CLASS_TEMPERATURE, DEVICE_CLASS_HUMIDITY)
|
||||
|
||||
ATTR_ATTRIBUTION, CONF_MONITORED_CONDITIONS, DEVICE_CLASS_HUMIDITY,
|
||||
DEVICE_CLASS_TEMPERATURE, TEMP_CELSIUS)
|
||||
from homeassistant.core import callback
|
||||
import homeassistant.helpers.config_validation as cv
|
||||
from homeassistant.helpers.dispatcher import async_dispatcher_connect
|
||||
from homeassistant.helpers.entity import Entity
|
||||
from homeassistant.helpers.icon import icon_for_battery_level
|
||||
|
||||
from . import ATTRIBUTION, DATA_ARLO, DEFAULT_BRAND, SIGNAL_UPDATE_ARLO
|
||||
|
||||
_LOGGER = logging.getLogger(__name__)
|
||||
|
||||
DEPENDENCIES = ['arlo']
|
||||
|
||||
1
homeassistant/components/aruba/__init__.py
Normal file
1
homeassistant/components/aruba/__init__.py
Normal file
@@ -0,0 +1 @@
|
||||
"""The aruba component."""
|
||||
1
homeassistant/components/arwn/__init__.py
Normal file
1
homeassistant/components/arwn/__init__.py
Normal file
@@ -0,0 +1 @@
|
||||
"""The arwn component."""
|
||||
1
homeassistant/components/asterisk_cdr/__init__.py
Normal file
1
homeassistant/components/asterisk_cdr/__init__.py
Normal file
@@ -0,0 +1 @@
|
||||
"""The asterisk_cdr component."""
|
||||
@@ -1,12 +1,13 @@
|
||||
"""Support for the Asterisk Voicemail interface."""
|
||||
import logging
|
||||
|
||||
from homeassistant.components.asterisk_mbox import DOMAIN as ASTERISK_DOMAIN
|
||||
from homeassistant.components.mailbox import (
|
||||
CONTENT_TYPE_MPEG, Mailbox, StreamError)
|
||||
from homeassistant.core import callback
|
||||
from homeassistant.helpers.dispatcher import async_dispatcher_connect
|
||||
|
||||
from . import DOMAIN as ASTERISK_DOMAIN
|
||||
|
||||
_LOGGER = logging.getLogger(__name__)
|
||||
|
||||
DEPENDENCIES = ['asterisk_mbox']
|
||||
|
||||
@@ -6,9 +6,10 @@ https://home-assistant.io/components/device_tracker.asuswrt/
|
||||
"""
|
||||
import logging
|
||||
|
||||
from homeassistant.components.asuswrt import DATA_ASUSWRT
|
||||
from homeassistant.components.device_tracker import DeviceScanner
|
||||
|
||||
from . import DATA_ASUSWRT
|
||||
|
||||
DEPENDENCIES = ['asuswrt']
|
||||
|
||||
_LOGGER = logging.getLogger(__name__)
|
||||
@@ -7,7 +7,8 @@ https://home-assistant.io/components/sensor.asuswrt/
|
||||
import logging
|
||||
|
||||
from homeassistant.helpers.entity import Entity
|
||||
from homeassistant.components.asuswrt import DATA_ASUSWRT
|
||||
|
||||
from . import DATA_ASUSWRT
|
||||
|
||||
DEPENDENCIES = ['asuswrt']
|
||||
|
||||
@@ -1,9 +1,10 @@
|
||||
"""Support for August binary sensors."""
|
||||
from datetime import datetime, timedelta
|
||||
import logging
|
||||
from datetime import timedelta, datetime
|
||||
|
||||
from homeassistant.components.august import DATA_AUGUST
|
||||
from homeassistant.components.binary_sensor import (BinarySensorDevice)
|
||||
from homeassistant.components.binary_sensor import BinarySensorDevice
|
||||
|
||||
from . import DATA_AUGUST
|
||||
|
||||
_LOGGER = logging.getLogger(__name__)
|
||||
|
||||
|
||||
@@ -3,9 +3,10 @@ from datetime import timedelta
|
||||
|
||||
import requests
|
||||
|
||||
from homeassistant.components.august import DATA_AUGUST, DEFAULT_TIMEOUT
|
||||
from homeassistant.components.camera import Camera
|
||||
|
||||
from . import DATA_AUGUST, DEFAULT_TIMEOUT
|
||||
|
||||
DEPENDENCIES = ['august']
|
||||
|
||||
SCAN_INTERVAL = timedelta(seconds=5)
|
||||
|
||||
@@ -1,11 +1,12 @@
|
||||
"""Support for August lock."""
|
||||
import logging
|
||||
from datetime import timedelta
|
||||
import logging
|
||||
|
||||
from homeassistant.components.august import DATA_AUGUST
|
||||
from homeassistant.components.lock import LockDevice
|
||||
from homeassistant.const import ATTR_BATTERY_LEVEL
|
||||
|
||||
from . import DATA_AUGUST
|
||||
|
||||
_LOGGER = logging.getLogger(__name__)
|
||||
|
||||
DEPENDENCIES = ['august']
|
||||
|
||||
1
homeassistant/components/aurora/__init__.py
Normal file
1
homeassistant/components/aurora/__init__.py
Normal file
@@ -0,0 +1 @@
|
||||
"""The aurora component."""
|
||||
16
homeassistant/components/auth/.translations/bg.json
Normal file
16
homeassistant/components/auth/.translations/bg.json
Normal file
@@ -0,0 +1,16 @@
|
||||
{
|
||||
"mfa_setup": {
|
||||
"totp": {
|
||||
"error": {
|
||||
"invalid_code": "\u041d\u0435\u0432\u0430\u043b\u0438\u0434\u0435\u043d \u043a\u043e\u0434, \u043c\u043e\u043b\u044f \u043e\u043f\u0438\u0442\u0430\u0439\u0442\u0435 \u043e\u0442\u043d\u043e\u0432\u043e. \u0410\u043a\u043e \u043f\u043e\u043b\u0443\u0447\u0430\u0432\u0430\u0442\u0435 \u0442\u0430\u0437\u0438 \u0433\u0440\u0435\u0448\u043a\u0430 \u043f\u043e\u0441\u0442\u043e\u044f\u043d\u043d\u043e, \u043c\u043e\u043b\u044f, \u0443\u0432\u0435\u0440\u0435\u0442\u0435 \u0441\u0435, \u0447\u0435 \u0447\u0430\u0441\u043e\u0432\u043d\u0438\u043a\u044a\u0442 \u043d\u0430 Home Assistant \u0435 \u0441\u0432\u0435\u0440\u0435\u043d."
|
||||
},
|
||||
"step": {
|
||||
"init": {
|
||||
"description": "\u0417\u0430 \u0434\u0430 \u0430\u043a\u0442\u0438\u0432\u0438\u0440\u0430\u0442\u0435 \u0434\u0432\u0443\u0444\u0430\u043a\u0442\u043e\u0440\u043d\u0430 \u0430\u0443\u0442\u0435\u043d\u0442\u0438\u043a\u0430\u0446\u0438\u044f \u043f\u043e\u0441\u0440\u0435\u0434\u0441\u0442\u0432\u043e\u043c \u0432\u0440\u0435\u043c\u0435\u0432\u043e-\u0431\u0430\u0437\u0438\u0440\u0430\u043d\u0438 \u0435\u0434\u043d\u043e\u043a\u0440\u0430\u0442\u043d\u0438 \u043f\u0430\u0440\u043e\u043b\u0438, \u0441\u043a\u0430\u043d\u0438\u0440\u0430\u0439\u0442\u0435 QR \u043a\u043e\u0434\u0430 \u0441 \u0430\u0443\u0442\u0435\u043d\u0442\u0438\u043a\u0430\u0442\u043e\u0440\u0430. \u0410\u043a\u043e \u043d\u044f\u043c\u0430\u0442\u0435 \u043f\u0440\u0438\u043b\u043e\u0436\u0435\u043d\u0438\u0435, \u0412\u0438 \u043f\u0440\u0435\u043f\u043e\u0440\u044a\u0447\u0432\u0430\u043c\u0435 \u0438\u043b\u0438 [Google Authenticator](https://support.google.com/accounts/answer/1066447) \u0438\u043b\u0438 [Authy](https://authy.com/).\n\n{qr_code}\n\n\u0421\u043b\u0435\u0434 \u0441\u043a\u0430\u043d\u0438\u0440\u0430\u043d\u0435 \u043d\u0430 \u043a\u043e\u0434\u0430, \u0432\u044a\u0432\u0435\u0434\u0435\u0442\u0435 6-\u0442\u0435 \u0446\u0438\u0444\u0440\u0438 \u043e\u0442 \u043f\u0440\u0438\u043b\u043e\u0436\u0435\u043d\u0438\u0435\u0442\u043e \u0437\u0430 \u0434\u0430 \u043f\u043e\u0442\u0432\u044a\u0440\u0434\u0438\u0442\u0435 \u0430\u043a\u0442\u0438\u0432\u0438\u0440\u0430\u043d\u0435\u0442\u043e. \u0410\u043a\u043e \u0438\u043c\u0430\u0442\u0435 \u043f\u0440\u043e\u0431\u043b\u0435\u043c\u0438 \u043f\u0440\u0438 \u0441\u043a\u0430\u043d\u0438\u0440\u0430\u043d\u0435\u0442\u043e \u043d\u0430 QR \u043a\u043e\u0434\u0430, \u043d\u0430\u043f\u0440\u0430\u0432\u0435\u0442\u0435 \u0440\u044a\u0447\u043d\u0430 \u043d\u0430\u0441\u0442\u0440\u043e\u0439\u043a\u0430 \u0441 \u043a\u043e\u0434 **`{code}`**.",
|
||||
"title": "\u041d\u0430\u0441\u0442\u0440\u043e\u0439\u043a\u0430 \u043d\u0430 \u0434\u0432\u0443\u0444\u0430\u043a\u0442\u043e\u0440\u043d\u0430 \u0430\u0443\u0442\u0435\u043d\u0442\u0438\u043a\u0430\u0446\u0438\u044f \u0447\u0440\u0435\u0437 TOTP"
|
||||
}
|
||||
},
|
||||
"title": "TOTP"
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -9,13 +9,15 @@
|
||||
},
|
||||
"step": {
|
||||
"init": {
|
||||
"description": "V\u00e1lassz \u00e9rtes\u00edt\u00e9si szolg\u00e1ltat\u00e1st:",
|
||||
"description": "K\u00e9rlek, v\u00e1lassz egyet az \u00e9rtes\u00edt\u00e9si szolg\u00e1ltat\u00e1sok k\u00f6z\u00fcl:",
|
||||
"title": "\u00c1ll\u00edtsa be az \u00e9rtes\u00edt\u00e9si \u00f6sszetev\u0151 \u00e1ltal megadott egyszeri jelsz\u00f3t"
|
||||
},
|
||||
"setup": {
|
||||
"description": "Az egyszeri jelsz\u00f3 el lett k\u00fcldve a(z) **notify.{notify_service}** szolg\u00e1ltat\u00e1ssal. K\u00e9rlek, add meg al\u00e1bb:",
|
||||
"title": "Be\u00e1ll\u00edt\u00e1s ellen\u0151rz\u00e9se"
|
||||
}
|
||||
}
|
||||
},
|
||||
"title": "Egyszeri Jelsz\u00f3 \u00c9rtes\u00edt\u00e9s"
|
||||
},
|
||||
"totp": {
|
||||
"error": {
|
||||
|
||||
@@ -81,7 +81,8 @@ from . import indieauth
|
||||
async def async_setup(hass, store_result):
|
||||
"""Component to allow users to login."""
|
||||
hass.http.register_view(AuthProvidersView)
|
||||
hass.http.register_view(LoginFlowIndexView(hass.auth.login_flow))
|
||||
hass.http.register_view(
|
||||
LoginFlowIndexView(hass.auth.login_flow, store_result))
|
||||
hass.http.register_view(
|
||||
LoginFlowResourceView(hass.auth.login_flow, store_result))
|
||||
|
||||
@@ -142,9 +143,10 @@ class LoginFlowIndexView(HomeAssistantView):
|
||||
name = 'api:auth:login_flow'
|
||||
requires_auth = False
|
||||
|
||||
def __init__(self, flow_mgr):
|
||||
def __init__(self, flow_mgr, store_result):
|
||||
"""Initialize the flow manager index view."""
|
||||
self._flow_mgr = flow_mgr
|
||||
self._store_result = store_result
|
||||
|
||||
async def get(self, request):
|
||||
"""Do not allow index of flows in progress."""
|
||||
@@ -179,6 +181,12 @@ class LoginFlowIndexView(HomeAssistantView):
|
||||
except data_entry_flow.UnknownStep:
|
||||
return self.json_message('Handler does not support init', 400)
|
||||
|
||||
if result['type'] == data_entry_flow.RESULT_TYPE_CREATE_ENTRY:
|
||||
result.pop('data')
|
||||
result['result'] = self._store_result(
|
||||
data['client_id'], result['result'])
|
||||
return self.json(result)
|
||||
|
||||
return self.json(_prepare_result_json(result))
|
||||
|
||||
|
||||
|
||||
1
homeassistant/components/automatic/__init__.py
Normal file
1
homeassistant/components/automatic/__init__.py
Normal file
@@ -0,0 +1 @@
|
||||
"""The automatic component."""
|
||||
@@ -6,20 +6,20 @@ import logging
|
||||
|
||||
import voluptuous as vol
|
||||
|
||||
from homeassistant.setup import async_prepare_setup_platform
|
||||
from homeassistant.core import CoreState, Context
|
||||
from homeassistant.loader import bind_hass
|
||||
from homeassistant.const import (
|
||||
ATTR_ENTITY_ID, CONF_PLATFORM, STATE_ON, SERVICE_TURN_ON, SERVICE_TURN_OFF,
|
||||
SERVICE_TOGGLE, SERVICE_RELOAD, EVENT_HOMEASSISTANT_START, CONF_ID,
|
||||
EVENT_AUTOMATION_TRIGGERED, ATTR_NAME)
|
||||
ATTR_ENTITY_ID, ATTR_NAME, CONF_ID, CONF_PLATFORM,
|
||||
EVENT_AUTOMATION_TRIGGERED, EVENT_HOMEASSISTANT_START, SERVICE_RELOAD,
|
||||
SERVICE_TOGGLE, SERVICE_TURN_OFF, SERVICE_TURN_ON, STATE_ON)
|
||||
from homeassistant.core import Context, CoreState
|
||||
from homeassistant.exceptions import HomeAssistantError
|
||||
from homeassistant.helpers import extract_domain_configs, script, condition
|
||||
from homeassistant.helpers import condition, extract_domain_configs, script
|
||||
import homeassistant.helpers.config_validation as cv
|
||||
from homeassistant.helpers.entity import ToggleEntity
|
||||
from homeassistant.helpers.entity_component import EntityComponent
|
||||
from homeassistant.helpers.restore_state import RestoreEntity
|
||||
from homeassistant.loader import bind_hass
|
||||
from homeassistant.setup import async_prepare_setup_platform
|
||||
from homeassistant.util.dt import utcnow
|
||||
import homeassistant.helpers.config_validation as cv
|
||||
|
||||
DOMAIN = 'automation'
|
||||
DEPENDENCIES = ['group']
|
||||
@@ -54,9 +54,8 @@ _LOGGER = logging.getLogger(__name__)
|
||||
def _platform_validator(config):
|
||||
"""Validate it is a valid platform."""
|
||||
try:
|
||||
platform = importlib.import_module(
|
||||
'homeassistant.components.automation.{}'.format(
|
||||
config[CONF_PLATFORM]))
|
||||
platform = importlib.import_module('.{}'.format(config[CONF_PLATFORM]),
|
||||
__name__)
|
||||
except ImportError:
|
||||
raise vol.Invalid('Invalid platform specified') from None
|
||||
|
||||
|
||||
1
homeassistant/components/avion/__init__.py
Normal file
1
homeassistant/components/avion/__init__.py
Normal file
@@ -0,0 +1 @@
|
||||
"""The avion component."""
|
||||
@@ -4,6 +4,7 @@ Support for Avion dimmers.
|
||||
For more details about this platform, please refer to the documentation at
|
||||
https://home-assistant.io/components/light.avion/
|
||||
"""
|
||||
import importlib
|
||||
import logging
|
||||
import time
|
||||
|
||||
@@ -38,7 +39,7 @@ PLATFORM_SCHEMA = PLATFORM_SCHEMA.extend({
|
||||
def setup_platform(hass, config, add_entities, discovery_info=None):
|
||||
"""Set up an Avion switch."""
|
||||
# pylint: disable=no-member
|
||||
import avion
|
||||
avion = importlib.import_module('avion')
|
||||
|
||||
lights = []
|
||||
if CONF_USERNAME in config and CONF_PASSWORD in config:
|
||||
@@ -108,7 +109,7 @@ class AvionLight(Light):
|
||||
def set_state(self, brightness):
|
||||
"""Set the state of this lamp to the provided brightness."""
|
||||
# pylint: disable=no-member
|
||||
import avion
|
||||
avion = importlib.import_module('avion')
|
||||
|
||||
# Bluetooth LE is unreliable, and the connection may drop at any
|
||||
# time. Make an effort to re-establish the link.
|
||||
1
homeassistant/components/awair/__init__.py
Normal file
1
homeassistant/components/awair/__init__.py
Normal file
@@ -0,0 +1 @@
|
||||
"""The awair component."""
|
||||
176
homeassistant/components/aws/__init__.py
Normal file
176
homeassistant/components/aws/__init__.py
Normal file
@@ -0,0 +1,176 @@
|
||||
"""Support for Amazon Web Services (AWS)."""
|
||||
import asyncio
|
||||
import logging
|
||||
from collections import OrderedDict
|
||||
|
||||
import voluptuous as vol
|
||||
|
||||
from homeassistant import config_entries
|
||||
from homeassistant.const import ATTR_CREDENTIALS, CONF_NAME, CONF_PROFILE_NAME
|
||||
from homeassistant.helpers import config_validation as cv, discovery
|
||||
|
||||
# Loading the config flow file will register the flow
|
||||
from . import config_flow # noqa
|
||||
from .const import (
|
||||
CONF_ACCESS_KEY_ID,
|
||||
CONF_CONTEXT,
|
||||
CONF_CREDENTIAL_NAME,
|
||||
CONF_CREDENTIALS,
|
||||
CONF_NOTIFY,
|
||||
CONF_REGION,
|
||||
CONF_SECRET_ACCESS_KEY,
|
||||
CONF_SERVICE,
|
||||
DATA_CONFIG,
|
||||
DATA_HASS_CONFIG,
|
||||
DATA_SESSIONS,
|
||||
DOMAIN,
|
||||
)
|
||||
|
||||
REQUIREMENTS = ["aiobotocore==0.10.2"]
|
||||
|
||||
_LOGGER = logging.getLogger(__name__)
|
||||
|
||||
AWS_CREDENTIAL_SCHEMA = vol.Schema(
|
||||
{
|
||||
vol.Required(CONF_NAME): cv.string,
|
||||
vol.Inclusive(CONF_ACCESS_KEY_ID, ATTR_CREDENTIALS): cv.string,
|
||||
vol.Inclusive(CONF_SECRET_ACCESS_KEY, ATTR_CREDENTIALS): cv.string,
|
||||
vol.Exclusive(CONF_PROFILE_NAME, ATTR_CREDENTIALS): cv.string,
|
||||
}
|
||||
)
|
||||
|
||||
DEFAULT_CREDENTIAL = [{CONF_NAME: "default", CONF_PROFILE_NAME: "default"}]
|
||||
|
||||
SUPPORTED_SERVICES = ["lambda", "sns", "sqs"]
|
||||
|
||||
NOTIFY_PLATFORM_SCHEMA = vol.Schema(
|
||||
{
|
||||
vol.Optional(CONF_NAME): cv.string,
|
||||
vol.Required(CONF_SERVICE): vol.All(
|
||||
cv.string, vol.Lower, vol.In(SUPPORTED_SERVICES)
|
||||
),
|
||||
vol.Required(CONF_REGION): vol.All(cv.string, vol.Lower),
|
||||
vol.Inclusive(CONF_ACCESS_KEY_ID, ATTR_CREDENTIALS): cv.string,
|
||||
vol.Inclusive(CONF_SECRET_ACCESS_KEY, ATTR_CREDENTIALS): cv.string,
|
||||
vol.Exclusive(CONF_PROFILE_NAME, ATTR_CREDENTIALS): cv.string,
|
||||
vol.Exclusive(CONF_CREDENTIAL_NAME, ATTR_CREDENTIALS): cv.string,
|
||||
vol.Optional(CONF_CONTEXT): vol.Coerce(dict),
|
||||
}
|
||||
)
|
||||
|
||||
CONFIG_SCHEMA = vol.Schema(
|
||||
{
|
||||
DOMAIN: vol.Schema(
|
||||
{
|
||||
vol.Optional(
|
||||
CONF_CREDENTIALS, default=DEFAULT_CREDENTIAL
|
||||
): vol.All(cv.ensure_list, [AWS_CREDENTIAL_SCHEMA]),
|
||||
vol.Optional(CONF_NOTIFY, default=[]): vol.All(
|
||||
cv.ensure_list, [NOTIFY_PLATFORM_SCHEMA]
|
||||
),
|
||||
}
|
||||
)
|
||||
},
|
||||
extra=vol.ALLOW_EXTRA,
|
||||
)
|
||||
|
||||
|
||||
async def async_setup(hass, config):
|
||||
"""Set up AWS component."""
|
||||
hass.data[DATA_HASS_CONFIG] = config
|
||||
|
||||
conf = config.get(DOMAIN)
|
||||
if conf is None:
|
||||
# create a default conf using default profile
|
||||
conf = CONFIG_SCHEMA({ATTR_CREDENTIALS: DEFAULT_CREDENTIAL})
|
||||
|
||||
hass.data[DATA_CONFIG] = conf
|
||||
hass.data[DATA_SESSIONS] = OrderedDict()
|
||||
|
||||
hass.async_create_task(
|
||||
hass.config_entries.flow.async_init(
|
||||
DOMAIN, context={"source": config_entries.SOURCE_IMPORT}, data=conf
|
||||
)
|
||||
)
|
||||
|
||||
return True
|
||||
|
||||
|
||||
async def async_setup_entry(hass, entry):
|
||||
"""Load a config entry.
|
||||
|
||||
Validate and save sessions per aws credential.
|
||||
"""
|
||||
config = hass.data.get(DATA_HASS_CONFIG)
|
||||
conf = hass.data.get(DATA_CONFIG)
|
||||
|
||||
if entry.source == config_entries.SOURCE_IMPORT:
|
||||
if conf is None:
|
||||
# user removed config from configuration.yaml, abort setup
|
||||
hass.async_create_task(
|
||||
hass.config_entries.async_remove(entry.entry_id)
|
||||
)
|
||||
return False
|
||||
|
||||
if conf != entry.data:
|
||||
# user changed config from configuration.yaml, use conf to setup
|
||||
hass.config_entries.async_update_entry(entry, data=conf)
|
||||
|
||||
if conf is None:
|
||||
conf = CONFIG_SCHEMA({DOMAIN: entry.data})[DOMAIN]
|
||||
|
||||
# validate credentials and create sessions
|
||||
validation = True
|
||||
tasks = []
|
||||
for cred in conf[ATTR_CREDENTIALS]:
|
||||
tasks.append(_validate_aws_credentials(hass, cred))
|
||||
if tasks:
|
||||
results = await asyncio.gather(*tasks, return_exceptions=True)
|
||||
for index, result in enumerate(results):
|
||||
name = conf[ATTR_CREDENTIALS][index][CONF_NAME]
|
||||
if isinstance(result, Exception):
|
||||
_LOGGER.error(
|
||||
"Validating credential [%s] failed: %s",
|
||||
name,
|
||||
result,
|
||||
exc_info=result,
|
||||
)
|
||||
validation = False
|
||||
else:
|
||||
hass.data[DATA_SESSIONS][name] = result
|
||||
|
||||
# set up notify platform, no entry support for notify component yet,
|
||||
# have to use discovery to load platform.
|
||||
for notify_config in conf[CONF_NOTIFY]:
|
||||
hass.async_create_task(
|
||||
discovery.async_load_platform(
|
||||
hass, "notify", DOMAIN, notify_config, config
|
||||
)
|
||||
)
|
||||
|
||||
return validation
|
||||
|
||||
|
||||
async def _validate_aws_credentials(hass, credential):
|
||||
"""Validate AWS credential config."""
|
||||
import aiobotocore
|
||||
|
||||
aws_config = credential.copy()
|
||||
del aws_config[CONF_NAME]
|
||||
|
||||
profile = aws_config.get(CONF_PROFILE_NAME)
|
||||
|
||||
if profile is not None:
|
||||
session = aiobotocore.AioSession(profile=profile, loop=hass.loop)
|
||||
del aws_config[CONF_PROFILE_NAME]
|
||||
if CONF_ACCESS_KEY_ID in aws_config:
|
||||
del aws_config[CONF_ACCESS_KEY_ID]
|
||||
if CONF_SECRET_ACCESS_KEY in aws_config:
|
||||
del aws_config[CONF_SECRET_ACCESS_KEY]
|
||||
else:
|
||||
session = aiobotocore.AioSession(loop=hass.loop)
|
||||
|
||||
async with session.create_client("iam", **aws_config) as client:
|
||||
await client.get_user()
|
||||
|
||||
return session
|
||||
22
homeassistant/components/aws/config_flow.py
Normal file
22
homeassistant/components/aws/config_flow.py
Normal file
@@ -0,0 +1,22 @@
|
||||
"""Config flow for AWS component."""
|
||||
|
||||
from homeassistant import config_entries
|
||||
|
||||
from .const import DOMAIN
|
||||
|
||||
|
||||
@config_entries.HANDLERS.register(DOMAIN)
|
||||
class AWSFlowHandler(config_entries.ConfigFlow):
|
||||
"""Handle a config flow."""
|
||||
|
||||
VERSION = 1
|
||||
CONNECTION_CLASS = config_entries.CONN_CLASS_CLOUD_PUSH
|
||||
|
||||
async def async_step_import(self, user_input):
|
||||
"""Import a config entry."""
|
||||
if self._async_current_entries():
|
||||
return self.async_abort(reason="single_instance_allowed")
|
||||
|
||||
return self.async_create_entry(
|
||||
title="configuration.yaml", data=user_input
|
||||
)
|
||||
16
homeassistant/components/aws/const.py
Normal file
16
homeassistant/components/aws/const.py
Normal file
@@ -0,0 +1,16 @@
|
||||
"""Constant for AWS component."""
|
||||
DOMAIN = "aws"
|
||||
|
||||
DATA_CONFIG = "aws_config"
|
||||
DATA_HASS_CONFIG = "aws_hass_config"
|
||||
DATA_SESSIONS = "aws_sessions"
|
||||
|
||||
CONF_ACCESS_KEY_ID = "aws_access_key_id"
|
||||
CONF_CONTEXT = "context"
|
||||
CONF_CREDENTIAL_NAME = "credential_name"
|
||||
CONF_CREDENTIALS = 'credentials'
|
||||
CONF_NOTIFY = "notify"
|
||||
CONF_PROFILE_NAME = "profile_name"
|
||||
CONF_REGION = "region_name"
|
||||
CONF_SECRET_ACCESS_KEY = "aws_secret_access_key"
|
||||
CONF_SERVICE = "service"
|
||||
243
homeassistant/components/aws/notify.py
Normal file
243
homeassistant/components/aws/notify.py
Normal file
@@ -0,0 +1,243 @@
|
||||
"""AWS platform for notify component."""
|
||||
import asyncio
|
||||
import base64
|
||||
import json
|
||||
import logging
|
||||
|
||||
from homeassistant.components.notify import (
|
||||
ATTR_TARGET,
|
||||
ATTR_TITLE,
|
||||
ATTR_TITLE_DEFAULT,
|
||||
BaseNotificationService,
|
||||
)
|
||||
from homeassistant.const import CONF_PLATFORM, CONF_NAME
|
||||
from homeassistant.helpers.json import JSONEncoder
|
||||
from .const import (
|
||||
CONF_CONTEXT,
|
||||
CONF_CREDENTIAL_NAME,
|
||||
CONF_PROFILE_NAME,
|
||||
CONF_REGION,
|
||||
CONF_SERVICE,
|
||||
DATA_SESSIONS,
|
||||
)
|
||||
|
||||
DEPENDENCIES = ["aws"]
|
||||
|
||||
_LOGGER = logging.getLogger(__name__)
|
||||
|
||||
|
||||
async def get_available_regions(hass, service):
|
||||
"""Get available regions for a service."""
|
||||
import aiobotocore
|
||||
|
||||
session = aiobotocore.get_session()
|
||||
# get_available_regions is not a coroutine since it does not perform
|
||||
# network I/O. But it still perform file I/O heavily, so put it into
|
||||
# an executor thread to unblock event loop
|
||||
return await hass.async_add_executor_job(
|
||||
session.get_available_regions, service
|
||||
)
|
||||
|
||||
|
||||
async def async_get_service(hass, config, discovery_info=None):
|
||||
"""Get the AWS notification service."""
|
||||
if discovery_info is None:
|
||||
_LOGGER.error('Please config aws notify platform in aws component')
|
||||
return None
|
||||
|
||||
import aiobotocore
|
||||
|
||||
session = None
|
||||
|
||||
conf = discovery_info
|
||||
|
||||
service = conf[CONF_SERVICE]
|
||||
region_name = conf[CONF_REGION]
|
||||
|
||||
available_regions = await get_available_regions(hass, service)
|
||||
if region_name not in available_regions:
|
||||
_LOGGER.error(
|
||||
"Region %s is not available for %s service, must in %s",
|
||||
region_name, service, available_regions
|
||||
)
|
||||
return None
|
||||
|
||||
aws_config = conf.copy()
|
||||
|
||||
del aws_config[CONF_SERVICE]
|
||||
del aws_config[CONF_REGION]
|
||||
if CONF_PLATFORM in aws_config:
|
||||
del aws_config[CONF_PLATFORM]
|
||||
if CONF_NAME in aws_config:
|
||||
del aws_config[CONF_NAME]
|
||||
if CONF_CONTEXT in aws_config:
|
||||
del aws_config[CONF_CONTEXT]
|
||||
|
||||
if not aws_config:
|
||||
# no platform config, use the first aws component credential instead
|
||||
if hass.data[DATA_SESSIONS]:
|
||||
session = next(iter(hass.data[DATA_SESSIONS].values()))
|
||||
else:
|
||||
_LOGGER.error(
|
||||
"Missing aws credential for %s", config[CONF_NAME]
|
||||
)
|
||||
return None
|
||||
|
||||
if session is None:
|
||||
credential_name = aws_config.get(CONF_CREDENTIAL_NAME)
|
||||
if credential_name is not None:
|
||||
session = hass.data[DATA_SESSIONS].get(credential_name)
|
||||
if session is None:
|
||||
_LOGGER.warning(
|
||||
"No available aws session for %s", credential_name
|
||||
)
|
||||
del aws_config[CONF_CREDENTIAL_NAME]
|
||||
|
||||
if session is None:
|
||||
profile = aws_config.get(CONF_PROFILE_NAME)
|
||||
if profile is not None:
|
||||
session = aiobotocore.AioSession(profile=profile, loop=hass.loop)
|
||||
del aws_config[CONF_PROFILE_NAME]
|
||||
else:
|
||||
session = aiobotocore.AioSession(loop=hass.loop)
|
||||
|
||||
aws_config[CONF_REGION] = region_name
|
||||
|
||||
if service == "lambda":
|
||||
context_str = json.dumps(
|
||||
{"custom": conf.get(CONF_CONTEXT, {})}, cls=JSONEncoder
|
||||
)
|
||||
context_b64 = base64.b64encode(context_str.encode("utf-8"))
|
||||
context = context_b64.decode("utf-8")
|
||||
return AWSLambda(session, aws_config, context)
|
||||
|
||||
if service == "sns":
|
||||
return AWSSNS(session, aws_config)
|
||||
|
||||
if service == "sqs":
|
||||
return AWSSQS(session, aws_config)
|
||||
|
||||
# should not reach here since service was checked in schema
|
||||
return None
|
||||
|
||||
|
||||
class AWSNotify(BaseNotificationService):
|
||||
"""Implement the notification service for the AWS service."""
|
||||
|
||||
def __init__(self, session, aws_config):
|
||||
"""Initialize the service."""
|
||||
self.session = session
|
||||
self.aws_config = aws_config
|
||||
|
||||
|
||||
class AWSLambda(AWSNotify):
|
||||
"""Implement the notification service for the AWS Lambda service."""
|
||||
|
||||
service = "lambda"
|
||||
|
||||
def __init__(self, session, aws_config, context):
|
||||
"""Initialize the service."""
|
||||
super().__init__(session, aws_config)
|
||||
self.context = context
|
||||
|
||||
async def async_send_message(self, message="", **kwargs):
|
||||
"""Send notification to specified LAMBDA ARN."""
|
||||
if not kwargs.get(ATTR_TARGET):
|
||||
_LOGGER.error("At least one target is required")
|
||||
return
|
||||
|
||||
cleaned_kwargs = {k: v for k, v in kwargs.items() if v is not None}
|
||||
payload = {"message": message}
|
||||
payload.update(cleaned_kwargs)
|
||||
json_payload = json.dumps(payload)
|
||||
|
||||
async with self.session.create_client(
|
||||
self.service, **self.aws_config
|
||||
) as client:
|
||||
tasks = []
|
||||
for target in kwargs.get(ATTR_TARGET, []):
|
||||
tasks.append(
|
||||
client.invoke(
|
||||
FunctionName=target,
|
||||
Payload=json_payload,
|
||||
ClientContext=self.context,
|
||||
)
|
||||
)
|
||||
|
||||
if tasks:
|
||||
await asyncio.gather(*tasks)
|
||||
|
||||
|
||||
class AWSSNS(AWSNotify):
|
||||
"""Implement the notification service for the AWS SNS service."""
|
||||
|
||||
service = "sns"
|
||||
|
||||
async def async_send_message(self, message="", **kwargs):
|
||||
"""Send notification to specified SNS ARN."""
|
||||
if not kwargs.get(ATTR_TARGET):
|
||||
_LOGGER.error("At least one target is required")
|
||||
return
|
||||
|
||||
message_attributes = {
|
||||
k: {"StringValue": json.dumps(v), "DataType": "String"}
|
||||
for k, v in kwargs.items()
|
||||
if v is not None
|
||||
}
|
||||
subject = kwargs.get(ATTR_TITLE, ATTR_TITLE_DEFAULT)
|
||||
|
||||
async with self.session.create_client(
|
||||
self.service, **self.aws_config
|
||||
) as client:
|
||||
tasks = []
|
||||
for target in kwargs.get(ATTR_TARGET, []):
|
||||
tasks.append(
|
||||
client.publish(
|
||||
TargetArn=target,
|
||||
Message=message,
|
||||
Subject=subject,
|
||||
MessageAttributes=message_attributes,
|
||||
)
|
||||
)
|
||||
|
||||
if tasks:
|
||||
await asyncio.gather(*tasks)
|
||||
|
||||
|
||||
class AWSSQS(AWSNotify):
|
||||
"""Implement the notification service for the AWS SQS service."""
|
||||
|
||||
service = "sqs"
|
||||
|
||||
async def async_send_message(self, message="", **kwargs):
|
||||
"""Send notification to specified SQS ARN."""
|
||||
if not kwargs.get(ATTR_TARGET):
|
||||
_LOGGER.error("At least one target is required")
|
||||
return
|
||||
|
||||
cleaned_kwargs = {k: v for k, v in kwargs.items() if v is not None}
|
||||
message_body = {"message": message}
|
||||
message_body.update(cleaned_kwargs)
|
||||
json_body = json.dumps(message_body)
|
||||
message_attributes = {}
|
||||
for key, val in cleaned_kwargs.items():
|
||||
message_attributes[key] = {
|
||||
"StringValue": json.dumps(val),
|
||||
"DataType": "String",
|
||||
}
|
||||
|
||||
async with self.session.create_client(
|
||||
self.service, **self.aws_config
|
||||
) as client:
|
||||
tasks = []
|
||||
for target in kwargs.get(ATTR_TARGET, []):
|
||||
tasks.append(
|
||||
client.send_message(
|
||||
QueueUrl=target,
|
||||
MessageBody=json_body,
|
||||
MessageAttributes=message_attributes,
|
||||
)
|
||||
)
|
||||
|
||||
if tasks:
|
||||
await asyncio.gather(*tasks)
|
||||
1
homeassistant/components/aws_lambda/__init__.py
Normal file
1
homeassistant/components/aws_lambda/__init__.py
Normal file
@@ -0,0 +1 @@
|
||||
"""The aws_lambda component."""
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user