Compare commits

..

968 Commits

Author SHA1 Message Date
Yegor Vialov
f0090d522d Merge pull request #566 from estevez-dev/rc/1.1.0-b3
Bump version code
2020-05-29 00:16:12 +03:00
Yegor Vialov
edbfd8359b Bump version code 2020-05-28 21:15:26 +00:00
Yegor Vialov
2702bb254a Bump version code 2020-05-28 20:55:08 +00:00
Yegor Vialov
ca7b6ed550 Resolves #564 - Show picture-elements as button if camera_image provided 2020-05-28 20:52:23 +00:00
Yegor Vialov
fb00b5d9ff Replace secure storage with encripted db 2020-05-28 20:23:13 +00:00
Yegor Vialov
7ffba397ce Fix crash whne no google play services available 2020-05-28 19:17:32 +00:00
Yegor Vialov
1080076e3b Merge pull request #562 from estevez-dev/rc/1.1.0-b2
Rc/1.1.0 b2
2020-05-25 18:59:27 +03:00
Yegor Vialov
e295a36465 1151 2020-05-25 15:57:57 +00:00
Yegor Vialov
9a09a83dc6 Request FCM token from native 2020-05-25 15:41:29 +00:00
Yegor Vialov
95ca80949f bump version code 2020-05-25 14:10:29 +00:00
Yegor Vialov
80b5763530 FCM token update and waiting 2020-05-25 14:09:45 +00:00
Yegor Vialov
9a5e35b024 Fix notification event data 2020-05-25 12:34:53 +00:00
Yegor Vialov
4493975676 defer fcm token load 2020-05-25 11:58:14 +00:00
Yegor Vialov
141a68faf7 Update readme 2020-05-25 11:34:55 +00:00
Yegor Vialov
a8efe7dbb6 Remove mobile app integration version check 2020-05-25 11:16:22 +00:00
Yegor Vialov
9608983994 Bump version code 2020-05-25 11:06:08 +00:00
Yegor Vialov
8eb15ab9a4 Notification channel description 2020-05-25 11:05:16 +00:00
Yegor Vialov
aac0cfbb56 Dismiss and auto dismiss for notifications 2020-05-25 10:20:48 +00:00
Yegor Vialov
343494ece0 Fix notification action receiver 2020-05-25 09:54:00 +00:00
Yegor Vialov
b1e5e73278 Fix display name getting issue 2020-05-25 08:52:16 +00:00
Yegor Vialov
9b5a0068fd Fix integer entity names handling in cards 2020-05-25 08:39:30 +00:00
Yegor Vialov
aa26212ddd bump version code 2020-05-25 08:22:52 +00:00
Yegor Vialov
1c45f96706 Remove Firebase hotfix
Firebase messages now handled by native code
2020-05-25 10:39:08 +03:00
Yegor Vialov
c2d5192c51 Filter strange card data 2020-05-25 10:37:02 +03:00
Yegor Vialov
88ae80507c Merge pull request #561 from estevez-dev/actionable_notifications
Actionable notifications
2020-05-25 02:34:18 +03:00
Yegor Vialov
55868d1dfe Resolves #471 Actionable notification 2020-05-24 23:33:20 +00:00
Yegor Vialov
92a1230267 WIP #471 Handling basic notifications in native code 2020-05-24 15:04:55 +00:00
Yegor Vialov
d3f99fb262 WIP #471 Native FCM init 2020-05-24 12:42:31 +00:00
Yegor Vialov
3fdf016c39 Merge pull request #560 from estevez-dev/rc/1.1.0
Rc/1.1.0
2020-05-24 11:26:53 +03:00
Yegor Vialov
8ce0e8aafa remove ndk filters 2020-05-24 08:26:09 +00:00
Yegor Vialov
54f6fb28ef add ndk platform filters 2020-05-24 07:57:00 +00:00
Yegor Vialov
d53825f140 Merge pull request #556 from estevez-dev/rc/1.1.0
Rc/1.1.0
2020-05-23 22:50:18 +03:00
Yegor Vialov
ea7e0f04ce bump build number 2020-05-23 19:27:57 +00:00
Yegor Vialov
cf75989447 trigger 2020-05-23 19:07:53 +00:00
Yegor Vialov
f27d55869b Prevent updates for dev builds 2020-05-23 16:26:42 +00:00
Yegor Vialov
a287f597ad Merge branch 'master' of https://github.com/estevez-dev/ha_client 2020-05-23 15:53:12 +00:00
Yegor Vialov
0698950f3d Changes for CD builds 2020-05-23 15:52:43 +00:00
Yegor Vialov
aa58559ba6 Update README.md 2020-05-23 18:47:59 +03:00
Yegor Vialov
94acc67383 1102 2020-05-23 14:28:43 +00:00
Yegor Vialov
701e6a46df 1.1.0 2020-05-22 16:01:16 +00:00
Yegor Vialov
1ed56ce8f1 Show entity icon on error loading entity picture 2020-05-22 13:31:07 +00:00
Yegor Vialov
a6d1baca77 Fix panel view scroll issue 2020-05-22 12:54:39 +00:00
Yegor Vialov
dbeda6ea68 Default states-like UI if no Lovelace config 2020-05-19 22:01:07 +00:00
Yegor Vialov
3dca28a7da Fix login button display 2020-05-19 21:05:45 +00:00
Yegor Vialov
da4264a409 Quick access buttons 2020-05-18 22:06:45 +00:00
Yegor Vialov
302451e118 Slider labels for light controls 2020-05-18 22:06:45 +00:00
Yegor Vialov
d19dbd389b Update README.md 2020-05-17 17:10:44 +03:00
Yegor Vialov
05ae954b30 Quick actions bar test 2020-05-17 11:16:25 +00:00
Yegor Vialov
840e266381 Update mobile app registration only on full load 2020-05-16 21:25:58 +00:00
Yegor Vialov
01525a2929 Update fcm lib 2020-05-16 21:23:15 +00:00
Yegor Vialov
2eb2596f37 Update push url 2020-05-16 20:04:04 +00:00
Yegor Vialov
699cab3498 Fix reconnect after app resume 2020-05-14 20:53:01 +00:00
Yegor Vialov
353c80b6bc Add path to every view 2020-05-14 16:44:50 +00:00
Yegor Vialov
e4d1a4f823 Resolves #212 2020-05-14 15:38:22 +00:00
Yegor Vialov
78d6b38b92 WIP #212 Light card 2020-05-14 12:13:23 +00:00
Yegor Vialov
1499a91ef7 SHow donate message only after 14 days of use 2020-05-14 11:20:06 +00:00
Yegor Vialov
9b7f7aa380 Fix slider possition change from outside 2020-05-14 10:56:52 +00:00
Yegor Vialov
5683ab5158 WIP: App settings refactoring 2020-05-13 12:46:25 +00:00
Yegor Vialov
a20dfaf05e WIP: AppSettings 2020-05-13 10:57:26 +00:00
Yegor Vialov
24d42c9597 Fix badge entity_picture size 2020-05-09 20:47:28 +00:00
Yegor Vialov
9078ad81e8 1100 2020-05-09 18:47:30 +00:00
Yegor Vialov
7cba6c8a10 Revert state filter as map fix 2020-05-09 18:46:53 +00:00
Yegor Vialov
c1f9c8c16d Fix for states in state filter not strings 2020-05-09 18:28:06 +00:00
Yegor Vialov
1d1d132b33 Resolves #530 Badges refactoring 2020-05-09 18:08:42 +00:00
Yegor Vialov
e258b3bc2c Avoid null for options in ModeSelectorWidget 2020-05-09 15:20:01 +00:00
Yegor Vialov
13508ee92f WIP #530 Badges refactoring 2020-05-09 13:38:05 +00:00
Yegor Vialov
4fbf58e707 Change default hold action to more-info 2020-05-09 11:39:53 +00:00
Yegor Vialov
a3442f84ca Glance card padding fixes 2020-05-08 14:23:35 +00:00
Yegor Vialov
6a6ab3b2cb Fix some padding inside cards 2020-05-08 13:59:04 +00:00
Yegor Vialov
d9fa553e2f Fix cions 2020-05-08 13:44:08 +00:00
Yegor Vialov
cde5d9b912 Fix cions 2020-05-08 13:42:25 +00:00
Yegor Vialov
3468446b5b Increase auth callback timeout for authenticated webview 2020-05-08 13:36:57 +00:00
Yegor Vialov
326434273a Disable external bus for authenticated webview 2020-05-08 13:29:54 +00:00
Yegor Vialov
470d3be946 Fix for empty stack cards 2020-05-08 13:20:01 +00:00
Yegor Vialov
d1032be6a6 Fix getting lovelace config for HA < 0.107 2020-05-08 12:59:06 +00:00
Yegor Vialov
cffac8e1f8 Safe display name parse 2020-05-08 12:55:44 +00:00
Yegor Vialov
870bc25dd9 Timer duration parsing error report improvement 2020-05-08 12:47:43 +00:00
Yegor Vialov
de713024f6 Safe icon parse 2020-05-08 12:37:53 +00:00
Yegor Vialov
4d4add4581 Dont parse castom cards. Hide unsupported 2020-05-08 12:36:13 +00:00
Yegor Vialov
1670c8e505 Hide unsupported cards 2020-05-07 16:14:23 +00:00
Yegor Vialov
55eb1b5125 Fix mistakes handling in gauge card 2020-05-07 15:48:20 +00:00
Yegor Vialov
dbeaaaf91e Fix null data referense for panels 2020-05-07 15:38:38 +00:00
Yegor Vialov
8166d8ce6d Fix null data referense 2020-05-07 15:33:45 +00:00
Yegor Vialov
35bcf0c1fa Fix nu data referense 2020-05-07 15:31:18 +00:00
Yegor Vialov
9c1d240962 1013 2020-05-06 19:05:11 +00:00
Yegor Vialov
a76652b552 1012 2020-05-06 18:46:05 +00:00
Yegor Vialov
a140f993d0 1011 2020-05-06 18:23:01 +00:00
Yegor Vialov
ded60a2867 not null 2020-05-06 18:22:17 +00:00
Yegor Vialov
b86602bcdb app version and whats new 2020-05-06 18:19:41 +00:00
Yegor Vialov
02ea45469f 1.0.1 2020-05-06 17:52:31 +00:00
Yegor Vialov
90105c3b09 Cache reading issues fix 2020-05-06 17:51:06 +00:00
Yegor Vialov
3d828914cc Fix error handling 2020-05-06 17:24:13 +00:00
Yegor Vialov
8cd5776bc6 Make integration work with HA < 0.104 2020-05-06 16:40:19 +00:00
Yegor Vialov
17ec73b176 Report foreground location errors 2020-05-06 16:19:42 +00:00
Yegor Vialov
e7cce01ca9 Remove beta 2020-05-05 12:34:20 +00:00
Yegor Vialov
6c73f5d979 1008 2020-05-05 12:30:41 +00:00
Yegor Vialov
f59cb5afbf Show error on registration fail 2020-05-05 12:25:34 +00:00
Yegor Vialov
5629215229 Fix app registration error reporting 2020-05-05 12:20:51 +00:00
Yegor Vialov
45fb637d48 Slider fixes. Also resolves #463 2020-05-05 12:12:49 +00:00
Yegor Vialov
7c473eb1ca Avoid null values for slider 2020-05-05 11:35:19 +00:00
Yegor Vialov
b40880c85a Remove some app integration errors 2020-05-05 11:33:04 +00:00
Yegor Vialov
30329ea3ba Update main.dart 2020-05-05 11:41:14 +03:00
Yegor Vialov
ca10401bee 1007 2020-05-04 14:50:23 +00:00
Yegor Vialov
814e0a8b00 Habdle mistakes in card config 2020-05-04 14:47:47 +00:00
Yegor Vialov
b5fbe7b86f Update main.dart 2020-05-04 14:51:55 +03:00
Yegor Vialov
fc9b6f05c0 Update main.dart 2020-05-04 14:51:41 +03:00
Yegor Vialov
eadae4374b Update whats_new.page.dart 2020-05-04 14:49:54 +03:00
Yegor Vialov
711cb04dcf 1.0.0-beta 2020-05-04 12:37:43 +03:00
Yegor Vialov
1d39b7fc7d Update pubspec.yaml 2020-05-04 12:37:01 +03:00
Yegor Vialov
2fa640433a Update whats_new.page.dart 2020-05-04 12:36:32 +03:00
Yegor Vialov
2445dc7869 Update startup_user_messages_manager.class.dart 2020-05-04 12:35:48 +03:00
Yegor Vialov
e3e114fe94 Resolves #551 2020-05-04 12:12:18 +03:00
Yegor Vialov
7a1603b423 alpha2 2020-05-04 12:11:26 +03:00
Yegor Vialov
4b831821da 1005 2020-05-04 12:10:40 +03:00
Yegor Vialov
1ec54953d7 Update .gitignore 2020-05-04 11:32:35 +03:00
Yegor Vialov
61571600d1 Delete google-services.json 2020-05-04 11:31:47 +03:00
Yegor Vialov
07a097aa50 Fix Discord link 2020-05-03 18:56:28 +00:00
Yegor Vialov
ce1cebaf64 Update README.md 2020-05-03 19:48:02 +03:00
Yegor Vialov
faf6f73b2a Update README.md 2020-05-03 19:47:49 +03:00
Yegor Vialov
db3b5d941e 1004 2020-05-03 15:27:22 +00:00
Yegor Vialov
cc60dc2b21 Fix token login popup show 2020-05-03 15:26:44 +00:00
Yegor Vialov
8aa0e03187 1.0.0 alpha1 2020-05-03 14:26:46 +00:00
Yegor Vialov
4492a08d6b battery updated to 1.0.0 2020-05-03 14:12:08 +00:00
Yegor Vialov
792c0d0c84 Resolves #545 handle hidden entities for Lovelace 2020-05-03 14:06:43 +00:00
Yegor Vialov
8221eceb78 Resolves #549 trim spaces for device name, url and long-lived token 2020-05-03 13:47:11 +00:00
Yegor Vialov
12ba5598e4 Resolves #547 Remove close button from quick start 2020-05-03 13:43:04 +00:00
Yegor Vialov
536cbf9445 Resolves #523 Change device name for integration 2020-05-03 13:36:40 +00:00
Yegor Vialov
a87943da27 Resolves #550 socket protocol set 2020-05-03 11:48:09 +00:00
Yegor Vialov
3fddc3b5a7 Resolves #546 Device name is missed 2020-05-03 11:25:09 +00:00
Yegor Vialov
5bc0b0868a 1002 2020-05-03 10:30:51 +00:00
Yegor Vialov
e9ad612fec Fix config panel opening 2020-05-03 10:00:32 +00:00
Yegor Vialov
c62e045dae flutter_secure_storage updated to 3.3.3 2020-05-03 09:58:49 +00:00
Yegor Vialov
725ec9291d WIP #523 and connection settings refactoring 2020-05-02 23:02:18 +00:00
Yegor Vialov
96c8338890 Show error when media_player is not media_player 2020-05-02 08:50:21 +00:00
Yegor Vialov
0996fb94da Fix getting entity on entity page when states is not loaded 2020-05-02 08:31:10 +00:00
Yegor Vialov
5de2431a0f Fix getting default icon of entity without id 2020-05-02 08:27:20 +00:00
Yegor Vialov
163338ea75 WIP #523 Change device name for integration 2020-05-01 22:39:32 +00:00
Yegor Vialov
f28e5493dc Popup menu position fix 2020-05-01 19:15:50 +00:00
Yegor Vialov
01c0a08fa8 Fix media menu closing behaviour 2020-05-01 19:02:42 +00:00
Yegor Vialov
1c461d2449 Fix camera stream view navigation issue 2020-05-01 18:50:50 +00:00
Yegor Vialov
915e8045a3 1001 2020-05-01 17:18:55 +00:00
Yegor Vialov
f10fc7eec1 Resolves #543: Double values in gauge config 2020-05-01 17:09:09 +00:00
Yegor Vialov
320bc677e0 Remove log viewer 2020-05-01 16:33:43 +00:00
Yegor Vialov
46ca1948e2 Error handling improvements 2020-05-01 16:24:13 +00:00
Yegor Vialov
7a0ce93cfd Fix wrong log level 2020-05-01 16:07:22 +00:00
Yegor Vialov
3c0bd68b0a Fix duplicating panels 2020-05-01 16:05:07 +00:00
Yegor Vialov
b4ad3061e4 Fix launchURLInBrowser 2020-05-01 15:49:50 +00:00
Yegor Vialov
d6b1fbec24 Open local navigate actions in browser 2020-05-01 15:48:54 +00:00
Yegor Vialov
cacdd0d304 Fix custom tab opening 2020-05-01 15:40:22 +00:00
Yegor Vialov
e3e1fa3499 Don't throw exception on cached data 2020-05-01 14:42:45 +00:00
Yegor Vialov
58842d1ebb Report all errors to Crashlytivs if in production 2020-05-01 14:37:05 +00:00
Yegor Vialov
101569d6ee Error reporting: HomeAssistant class 2020-05-01 14:37:05 +00:00
Yegor Vialov
8a180c4c0e Panels error handling 2020-05-01 14:37:05 +00:00
estevez-dev
ba343fbd98 Final icons update for ald Android versions 2020-04-30 17:14:38 +03:00
Yegor Vialov
1d528df341 Adoptive icon fixes 2020-04-30 14:04:37 +00:00
estevez-dev
51ea0b0afa Icons update 2020-04-30 15:52:00 +03:00
Yegor Vialov
9dbb697e58 Adoptive app icon 2020-04-30 12:43:56 +00:00
estevez-dev
947558bb3d Icons update 2020-04-30 15:08:26 +03:00
Yegor Vialov
8ba4cc85d8 Bump version to 1.0.0 for fist stable release 2020-04-30 11:23:20 +00:00
estevez-dev
0f604a6ce6 New app icon 2020-04-30 13:03:16 +03:00
Yegor Vialov
7e48c6535f 898 2020-04-29 22:39:47 +00:00
Yegor Vialov
1d2a8b613b Disable Crashlytics in debug mode 2020-04-29 22:37:12 +00:00
Yegor Vialov
89e833eb33 Fix theme settings loading 2020-04-29 22:30:53 +00:00
Yegor Vialov
b65a68e0c4 Help pages links 2020-04-29 22:28:52 +00:00
Yegor Vialov
bfb24b9d11 Call service state color from theme 2020-04-29 20:11:37 +00:00
Yegor Vialov
0792cae2b1 Fix missed entity picture 2020-04-29 20:04:59 +00:00
Yegor Vialov
a85fb3d03b Display light color as a badge on entity icon 2020-04-29 20:01:37 +00:00
Yegor Vialov
ddb9a9d4e9 Fix gauge card tap events 2020-04-29 17:48:15 +00:00
Yegor Vialov
29ee360ec4 Remove test exception 2020-04-29 07:49:46 +00:00
Yegor Vialov
c0faaafd04 0.8.6 2020-04-29 07:48:05 +00:00
Yegor Vialov
bc045344a5 Handle card rendering errors to show in ui 2020-04-29 07:45:15 +00:00
Yegor Vialov
7d746fd546 896 2020-04-29 07:11:09 +00:00
Yegor Vialov
3ff55f181e Fix icon_height parsing issue 2020-04-29 10:00:28 +03:00
Yegor Vialov
187e12dd79 895 2020-04-29 01:19:43 +03:00
Yegor Vialov
10daf2d952 0.8.5 2020-04-28 21:47:41 +00:00
Yegor Vialov
31c6509d13 0.8.5 2020-04-28 21:43:48 +00:00
Yegor Vialov
cb74108814 893 2020-04-28 21:12:18 +00:00
Yegor Vialov
9efded2139 Possible workaround for firebase messaging crash 2020-04-28 21:09:45 +00:00
Yegor Vialov
96b3e7c739 Add state_color support 2020-04-28 21:03:00 +00:00
Yegor Vialov
b029146bf3 Gauge scale fixes 2020-04-28 20:36:11 +00:00
Yegor Vialov
d715aaf5e5 Gauge card elements scale fix 2020-04-27 20:56:33 +00:00
Yegor Vialov
0dc12963f0 Cards parsing improvements 2020-04-26 22:46:37 +00:00
Yegor Vialov
4da3b40d55 WIP: Cards parsing improvements 2020-04-26 18:44:21 +00:00
Yegor Vialov
f7d05a57ad Use '***' in markdawn card to add empty space 2020-04-25 21:45:58 +00:00
Yegor Vialov
df01599fe0 Resloves #541 Prevent double service call when slider is moved 2020-04-25 21:24:15 +00:00
Yegor Vialov
2c3335ebf3 Resolves #539 Fix button card without entity 2020-04-25 20:53:08 +00:00
Yegor Vialov
05c1427aa8 Add icon support for entities card title 2020-04-25 18:23:26 +00:00
Yegor Vialov
02bfaf7db6 WIP: Cards build optimization 2020-04-25 17:38:21 +00:00
Yegor Vialov
f488c0810b WIP: Cards build optimization 2020-04-25 15:59:07 +00:00
Yegor Vialov
8dbfb91234 Add Lovelase card widget 2020-04-25 14:53:33 +00:00
Yegor Vialov
aee99e3925 Entities card build optimization 2020-04-25 14:39:15 +00:00
Yegor Vialov
50d3280803 Gauge font size fix 2020-04-25 14:33:54 +00:00
Yegor Vialov
a90eb5c4db Fix glance card title 2020-04-25 14:20:46 +00:00
Yegor Vialov
16c06a2d48 Widget rendering improvements 2020-04-25 14:15:19 +00:00
Yegor Vialov
513bf85cae Fix dashboard switching issues 2020-04-21 09:01:21 +00:00
Yegor Vialov
82d7aeba02 Some cleanup 2020-04-15 18:23:29 +00:00
Yegor Vialov
12f7cb86de Linear progress indicator 2020-04-15 18:21:16 +00:00
Yegor Vialov
b65c885467 Bottom info bar as standalone component 2020-04-15 18:13:03 +00:00
Yegor Vialov
2a828a1289 WIP: bottom info bar as separate component 2020-04-15 15:40:21 +00:00
Yegor Vialov
291f12ba97 Component detection 2020-04-15 15:40:21 +00:00
Yegor Vialov
6afbd37d71 892 2020-04-15 15:46:06 +03:00
Yegor Vialov
0e8869878f 0.8.4 2020-04-15 12:06:06 +00:00
Yegor Vialov
7c2cfe3215 MobileApp integration force update 2020-04-15 11:59:40 +00:00
Yegor Vialov
c376c0e952 Minor fix 2020-04-14 21:08:10 +00:00
Yegor Vialov
da5f663396 Gauge card optimizations 2020-04-14 18:39:21 +00:00
Yegor Vialov
0e92418a33 Handle missed entity on Entity page 2020-04-14 18:15:28 +00:00
Yegor Vialov
2eef7cfe5e Improve Horizontal and Vertical stack building 2020-04-13 19:24:37 +00:00
Yegor Vialov
de4e0bfb3a Add new Button card support 2020-04-13 18:17:14 +00:00
Yegor Vialov
8bf2d31e72 Bring back separate entity page 2020-04-13 18:15:14 +00:00
Yegor Vialov
2125c46143 Gauge style improvements 2020-04-13 14:01:17 +00:00
Yegor Vialov
5402eb84df Some default icons update 2020-04-13 13:13:49 +00:00
Yegor Vialov
ad5aa0898f Set header toggle default to false 2020-04-13 13:05:59 +00:00
Yegor Vialov
040d40b614 PayPal donate button 2020-04-11 19:26:25 +00:00
Yegor Vialov
8e58f22c56 Merge pull request #533 from estevez-dev/pre-release/889
890
2020-04-11 21:16:12 +03:00
Yegor Vialov
c91695fbe5 890 2020-04-11 21:15:08 +03:00
Yegor Vialov
c43741da49 889 2020-04-11 20:28:35 +03:00
Yegor Vialov
f2563a0397 Fix startup crash 2020-04-11 20:25:54 +03:00
Yegor Vialov
fba4017819 0.8.3 2020-04-11 16:55:50 +00:00
Yegor Vialov
5f23e108a1 Settings page and theme selection 2020-04-11 16:09:35 +00:00
Yegor Vialov
68d14bd13d Fallback to MJPEG camera stream if streaming component is missing 2020-04-11 13:13:20 +00:00
Yegor Vialov
022622522f Fix section text color for dark theme 2020-04-11 12:48:36 +00:00
Yegor Vialov
89513ca4e5 Secrets config for CI/CD 2020-04-09 17:10:21 +00:00
Yegor Vialov
a934ee2335 Hide zha panel 2020-04-09 16:25:35 +00:00
Yegor Vialov
49aeea634f 0.8.2 2020-04-09 16:24:37 +00:00
Yegor Vialov
e18b9ebe14 Replace VideoPlayer with web player 2020-04-08 16:48:13 +00:00
Yegor Vialov
08ee3f3d80 Fix inactive gauge color 2020-04-07 20:30:39 +00:00
Yegor Vialov
62d07bf8b9 Gauge card refactoring 2020-04-07 20:20:57 +00:00
Yegor Vialov
ab398cbdc3 Remove debugg banner 2020-04-06 21:43:38 +00:00
Yegor Vialov
007d12719c WIP #102 Moving all colors to theme 2020-04-06 21:39:16 +00:00
Yegor Vialov
524d195800 WIP #102 Colors from theme 2020-04-06 20:03:41 +00:00
Yegor Vialov
405de64249 Fix double events handling 2020-04-04 22:34:54 +00:00
Yegor Vialov
f53554702e WIP Themes: new dark theme 2020-04-04 22:08:50 +00:00
Yegor Vialov
379e1a4a7e WIP Themes: State colors from themes 2020-04-04 21:39:12 +00:00
Yegor Vialov
d6f7096055 WIP Themes: New light theme 2020-04-04 20:54:32 +00:00
Yegor Vialov
37c721e4f6 WIP Themes: Entity page heade color 2020-04-04 16:19:25 +00:00
Yegor Vialov
d94235ef6d WIP Themes: Dark theme fonts 2020-04-04 16:13:12 +00:00
Yegor Vialov
eb4184713f WIP Themes: badges colors 2020-04-04 15:44:06 +00:00
Yegor Vialov
a0a0cb4612 WIP Themes: Make all fonts depend on theme 2020-04-04 15:13:55 +00:00
Yegor Vialov
f448a20784 WIP Themes: primary colors fix 2020-04-04 13:36:32 +00:00
Yegor Vialov
36eff26862 WIP themes: fix sudhead style 2020-04-04 13:27:23 +00:00
Yegor Vialov
5b2a1163b9 Merge pull request #529 from estevez-dev/hotfix/0.8.1
Fix for older HA versions
2020-04-04 16:22:24 +03:00
Yegor Vialov
e627a8b963 Fix for older HA versions 2020-04-04 13:17:01 +00:00
Yegor Vialov
4432124e8c WIP Themes: font size standartization 2020-04-04 12:47:40 +00:00
Yegor Vialov
b8ba3c59e9 WIP Themes: climate controls and entity name 2020-04-04 12:00:15 +00:00
Yegor Vialov
c40a496b6b Replace Spectrum with Discord 2020-04-04 10:12:21 +00:00
Yegor Vialov
a7c3b46061 Update pubspec.yaml 2020-04-03 17:25:59 +03:00
Yegor Vialov
dfbaaeb06b 0.8.0 2020-04-03 13:32:23 +00:00
Yegor Vialov
f6ab20c6e8 Add whats new manu item 2020-04-03 13:29:02 +00:00
Yegor Vialov
7625099d74 Disable background location log 2020-04-03 13:17:24 +00:00
Yegor Vialov
32c8e76855 Fix for non-string climate mode values 2020-04-03 13:07:26 +00:00
Yegor Vialov
0aa2c974d5 Disable crashlytics in debug mode 2020-04-03 12:43:48 +00:00
Yegor Vialov
9524c8587b Update WHats new link 2020-04-03 12:36:16 +00:00
Yegor Vialov
c075db8b1a 883 2020-04-02 22:05:36 +00:00
Yegor Vialov
d0b7cc1929 Initial UI generation if no lovelace config 2020-04-02 22:04:56 +00:00
Yegor Vialov
d8df32f140 Resolves #524 Geolocator update 2020-04-02 21:16:34 +00:00
Yegor Vialov
293b5e0242 zha_map WIP: get inital data 2020-04-01 17:04:32 +00:00
Yegor Vialov
2f517a3ad5 ui build refactoring 1 2020-03-21 23:11:00 +00:00
Yegor Vialov
56d8e389db Do not update plugins on each gitpod start 2020-03-21 20:45:01 +00:00
Yegor Vialov
1377843350 882 2020-03-21 14:06:58 +00:00
Yegor Vialov
8e31eaf8bb Fix dashboard switching and dashboard icons 2020-03-20 10:14:14 +00:00
Yegor Vialov
5ced01463f Resolves #526 Subscribe to Lovelace update events 2020-03-19 23:16:59 +00:00
Yegor Vialov
a3548455eb Resolves #525 Support Lovelace dashboards 2020-03-19 22:59:53 +00:00
Yegor Vialov
c40fceea4f add log output on location error in background 2020-03-15 17:51:01 +00:00
Yegor Vialov
6ad3938a91 Resolves #369 Get actual entity data in entity wrapper 2020-03-15 16:28:45 +00:00
Yegor Vialov
bc642f81ad Fix caching 2020-03-15 15:26:03 +00:00
Yegor Vialov
14ce608bbb Add data caching 2020-03-15 13:47:51 +00:00
Yegor Vialov
c4c67747c5 Remove services requesting 2020-03-15 12:34:16 +00:00
Yegor Vialov
5b3ceecb0e Update crashlytics 2020-03-15 12:27:32 +00:00
Yegor Vialov
bf53e4b9df Add fit option for EntityPicture widget 2020-03-14 18:23:39 +00:00
Yegor Vialov
7e09d92fdf Add double_tap_action support 2020-03-14 18:12:11 +00:00
Yegor Vialov
1ba9106d0b Show entity picture while camera stream loading 2020-03-14 17:56:07 +00:00
Yegor Vialov
d727a29991 881 2020-03-12 21:20:46 +00:00
Yegor Vialov
c5d617477f Fix state filter when state is number 2020-03-12 21:19:39 +00:00
Yegor Vialov
244a1984cc Resolves #512 Support all state_filter options 2020-03-12 21:16:07 +00:00
Yegor Vialov
b00b745f27 Camera fullscreen view 2020-03-12 12:22:38 +00:00
Yegor Vialov
959ff21b9b Fix checking log file size if it doesnt exist 2020-03-09 15:01:53 +00:00
Yegor Vialov
e6a7fd2dfe workmanager 0.2.2, geolocator 5.3.0, crashlytics 0.1.3 2020-03-09 14:57:29 +00:00
Yegor Vialov
216276e5f3 Limit log file size to 5MB. Background location fixes 2020-03-09 14:53:12 +00:00
Yegor Vialov
3e6229cf3e Add log file for background service 2020-03-09 13:11:16 +00:00
Yegor Vialov
fc4cb80b74 Fix background task execution 2020-03-05 09:08:47 +00:00
Yegor Vialov
b907ff1e82 Location isolate return 2020-03-04 17:16:16 +00:00
Yegor Vialov
7536a52771 Clear camera viewer 2020-03-04 16:59:08 +00:00
Yegor Vialov
73a8c111d1 Camera stream controls 2020-02-21 15:36:03 +00:00
Yegor Vialov
86a19eeec2 Camera stream web view improvements 2020-02-21 11:23:39 +00:00
Yegor Vialov
fba4459977 Camera stream view fixes 2020-02-20 22:42:19 +00:00
Yegor Vialov
06f994a827 Camera stream aspect ratio calculations 2020-02-20 21:48:22 +00:00
Yegor Vialov
35d8607484 Resolves #370 Camera stream support 2020-02-20 14:33:03 +00:00
Yegor Vialov
2f4c06e9b5 Resolves #420 Camera view on entity page 2020-02-20 11:18:09 +00:00
Yegor Vialov
92e008a380 Resolves #522 Fix temperature change for thermostat 2020-02-19 14:20:44 +00:00
Yegor Vialov
14c272af92 Resolves #501 Entities card header toggle 2020-02-19 10:17:08 +00:00
Yegor Vialov
710de9f2b8 Resolves #514 Media player volume buttons placement 2020-02-19 09:27:01 +00:00
Yegor Vialov
d9ad3b3083 Location tracking... 2020-02-17 19:23:00 +00:00
Yegor Vialov
b2686cb105 Update README.md 2020-02-17 16:39:30 +02:00
Yegor Vialov
959e89de2b Remove web UI presentation option 2020-02-17 13:50:24 +00:00
Yegor Vialov
6e448d3458 JS interface improvements 2020-02-12 22:42:43 +00:00
Yegor Vialov
6695756727 Embedded webview 2020-02-12 21:13:49 +00:00
Yegor Vialov
ed732e9b77 Remove bottom bar for webview 2020-02-12 13:37:52 +00:00
Yegor Vialov
f495a6affc Move to ha-client.app 2020-02-12 13:22:48 +00:00
Yegor Vialov
c8d7e1a95f Update README.md 2020-02-12 12:39:36 +02:00
Yegor Vialov
e1ca2638e3 External bus and configuration opening for Web UI 2020-02-11 22:55:16 +00:00
Yegor Vialov
01226cb9eb WebVIew UI settings 2020-02-11 20:53:29 +00:00
Yegor Vialov
8a80d0c5d1 WebView UI experiment 001 2020-02-11 14:02:32 +00:00
Yegor Vialov
f26f3e87c7 Remove Crashlytics debug 2020-02-11 12:57:31 +00:00
Yegor Vialov
b750417415 Replace Sentry with Crashlytics 2020-02-11 12:06:19 +00:00
Yegor Vialov
2c35dd7c21 Update battery plugin 2020-02-11 11:25:58 +00:00
Yegor Vialov
cff4a4feed Update workmanager and migrate to Flutter embedding v2 2020-02-11 11:22:59 +00:00
Yegor Vialov
62174b0651 Update geolocator plugin 2020-02-11 11:07:13 +00:00
Yegor Vialov
d3ea4210c1 Update local notifications library 2020-02-11 11:03:39 +00:00
Yegor Vialov
1c782bf64d Update firebase library and downgrade gradle 2020-02-11 10:57:15 +00:00
Yegor Vialov
bc96dab339 Update gradle and Google services 2020-02-11 09:52:04 +00:00
Yegor Vialov
0f7179b944 In app purchase update and optimizations 2020-02-11 09:39:11 +00:00
Yegor Vialov
1e3bfa8ff7 Install Java in gitpod with sdkman 2020-02-07 09:46:34 +00:00
Yegor Vialov
2bce86f905 gitpod config 2020-02-07 09:27:23 +00:00
Yegor Vialov
0be00acc3a do 2020-01-30 20:50:42 +00:00
Yegor Vialov
4e61adaeb1 Fixind java version switch 2020-01-30 20:42:29 +00:00
Yegor Vialov
49a8f08153 Update .gitpod.dockerfile 2020-01-30 22:34:10 +02:00
Yegor Vialov
ce15658462 Add jdk version set 2020-01-30 20:28:34 +00:00
Yegor Vialov
16d73ba7dd Update .gitpod.yml 2020-01-30 22:20:54 +02:00
Yegor Vialov
9f3e3c1917 Update .gitpod.dockerfile 2020-01-30 22:20:01 +02:00
Yegor Vialov
f29e382a19 Update .gitpod.yml 2020-01-30 21:53:29 +02:00
Yegor Vialov
073562373a Update .gitpod.dockerfile 2020-01-30 21:43:28 +02:00
Yegor Vialov
4298ebcd66 Bump Flutter version to 1.12.13 2020-01-29 20:13:28 +02:00
Yegor Vialov
a121295bef Update .gitignore 2020-01-29 20:12:46 +02:00
Yegor Vialov
9303e4c0a5 Force https for oauth 2020-01-29 17:38:51 +00:00
Yegor Vialov
831fc98ab1 Change domain for ha-client website 2020-01-29 17:31:16 +00:00
Yegor Vialov
2003005e56 Update README.md 2020-01-29 11:48:32 +02:00
Yegor Vialov
fda8fb7182 Webview for external panels and everything 2020-01-27 21:25:55 +00:00
Yegor Vialov
cf6039b279 Back to webview oauth 2020-01-27 21:12:05 +00:00
Yegor Vialov
41e552dce5 New webview test 2020-01-27 20:06:02 +00:00
Yegor Vialov
90043b5806 Remove text sharing feature 2020-01-27 19:21:21 +00:00
Yegor Vialov
9eb74b5a8d WIP Share media refactoring 2019-12-11 18:24:09 +00:00
Yegor Vialov
9cc60a136b 0.7.7 2019-12-10 22:05:12 +00:00
Yegor Vialov
78eb1e779c Fix state event handling before fetch complete 2019-12-10 22:04:24 +00:00
Yegor Vialov
8db2d8508e Resolves #515 tabs controller issue 2019-12-10 22:00:17 +00:00
Yegor Vialov
3f1ece26ec Remove background task logging and reporting 2019-12-10 21:19:29 +00:00
Yegor Vialov
d1912a44c6 Replace Discord with Spectrum 2019-12-10 19:54:02 +00:00
Yegor Vialov
36a05eb390 Update README.md 2019-12-06 10:58:23 +02:00
Yegor Vialov
4f39ea1ad8 Update README.md 2019-12-04 16:31:03 +02:00
Yegor Vialov
a241cc1d61 Resolves #494 2019-11-29 13:40:51 +00:00
Yegor Vialov
8b4df98cb9 0.7.6 2019-11-29 13:02:22 +00:00
Yegor Vialov
7d30c2f9d5 Wrap empty navigate action 2019-11-29 12:45:59 +00:00
Yegor Vialov
44acabadfe Fix location tracking backfroud task id 2019-11-29 11:27:59 +00:00
Yegor Vialov
6f3a2bb78d Fix timeout handling on socket message send 2019-11-29 11:24:29 +00:00
Yegor Vialov
dd5f8b155d Handle some socket exceptions 2019-11-29 11:21:45 +00:00
Yegor Vialov
cd81fc72fd Fix connection timeout handling 2019-11-29 10:58:24 +00:00
Yegor Vialov
890da650dc Resolves #508 show_name for enriry button card 2019-11-29 10:12:41 +00:00
Yegor Vialov
9897b6a44b Fix show_empty for entity-filter 2019-11-29 10:05:09 +00:00
Yegor Vialov
7969f54d3b Fix MissedPluginException for workmanager 2019-11-28 19:54:22 +00:00
Yegor Vialov
7c18454de3 Fix issue with handling service call exceptions 2019-11-28 19:14:50 +00:00
Yegor Vialov
dcf5efddd1 Parse port and protocol from HA url 2019-11-28 18:57:41 +00:00
Yegor Vialov
a6541134e0 Fix compliting alrady completed future 2019-11-28 18:33:27 +00:00
Yegor Vialov
90504047b4 Resolves #492 Infinity media player progress error 2019-11-28 17:42:48 +00:00
Yegor Vialov
ca1eec6602 Update bug_report.md 2019-11-27 18:44:45 +02:00
Yegor Vialov
edc01d14b7 Resolves #511 2019-11-27 17:21:23 +02:00
Yegor Vialov
6cb5463b13 minor background reporting fix 2019-11-27 14:50:53 +00:00
Yegor Vialov
63a789ebfb 0.7.5 name fix 2019-11-27 12:48:03 +00:00
Yegor Vialov
a0994e9a60 0.7.5 2019-11-27 12:42:09 +00:00
Yegor Vialov
8d1b728194 Background location tracking crash reporting 2019-11-27 12:41:38 +00:00
Yegor Vialov
1a9fec8b98 Senty reporting. Fix background location tracking crash 2019-11-27 12:26:55 +00:00
Yegor Vialov
e634253282 0.7.4 2019-11-26 21:13:59 +00:00
Yegor Vialov
64b23ec7cc Revert flutter_markdown to 0.3.0 2019-11-26 21:13:07 +00:00
Yegor Vialov
afe207a878 Removes foreground location and Resolves #510 2019-11-26 20:56:24 +00:00
Yegor Vialov
4bac0c092f Removes foreground location and Resolves #510 2019-11-26 20:54:36 +00:00
Yegor Vialov
74c8ae35a1 Remove network security config 2019-11-26 20:48:08 +00:00
Yegor Vialov
7856637456 Fix app version display 2019-11-26 16:50:19 +00:00
Yegor Vialov
965f80a6ca 0.7.3 2019-11-14 12:58:56 +02:00
Yegor Vialov
198c2ba49a build 730 2019-11-14 12:58:32 +02:00
Yegor Vialov
4b9ec5ca6e Foreground location updates 2019-11-10 21:53:28 +00:00
Yegor Vialov
5792652619 Experimental location tracking for every 10 or 5 minutes 2019-11-10 14:41:29 +00:00
Yegor Vialov
2c900333a5 WIP #344 Add network security config to allow user certificates 2019-11-10 13:53:25 +00:00
Yegor Vialov
1f782d7cd3 Resolves #498 Handle bool state 2019-11-10 13:48:05 +00:00
Yegor Vialov
89cc1833de Resolves #493 Send media_player calls even if it if unavailable 2019-11-08 20:50:31 +00:00
Yegor Vialov
1262d8c9aa Resolves #484 Fix entity-filter cards 2019-11-08 20:41:51 +00:00
Yegor Vialov
85b0c4f814 Resolves #419 Fallback to states if no lovelace config found 2019-11-08 20:14:34 +00:00
Yegor Vialov
551a8dfa31 Fixx service calls 2019-11-08 19:37:41 +00:00
Yegor Vialov
139533d2ca 0.7.2 2019-11-01 14:00:13 +00:00
Yegor Vialov
889682f771 Resolves #491 Lovelace badges parse issue 2019-11-01 13:54:35 +00:00
Yegor Vialov
f16c98057f Location fixes 2019-11-01 13:44:51 +00:00
Yegor Vialov
26ec807c25 Resolves #490 Prevent fused coarse location 2019-10-30 16:54:25 +00:00
Yegor Vialov
45af6cbe3c Fix play_media call 2019-10-30 15:04:23 +00:00
Yegor Vialov
5dd9cde12d Entity page fixes 2019-10-30 14:25:30 +00:00
Yegor Vialov
472fb1d367 Merge pull request #489 from estevez-dev/hotfix/0.7.1
Hotfix/0.7.1
2019-10-29 19:47:33 +02:00
Yegor Vialov
8b372fbc0b Merge branch 'master' into hotfix/0.7.1 2019-10-29 19:47:24 +02:00
Yegor Vialov
40d72eb6e1 0.7.1 2019-10-29 17:46:16 +00:00
Yegor Vialov
ced008a7c1 Resolves #486 Fix for very small screens 2019-10-29 17:44:18 +00:00
Yegor Vialov
d1f652282a Merge pull request #488 from estevez-dev/pre-release/0.7.1
Pre release/0.7.1
2019-10-29 19:35:10 +02:00
Yegor Vialov
f656528d5b 711 2019-10-28 20:42:05 +00:00
Yegor Vialov
bcdb2a648c Fix entity page linking after app resumed 2019-10-28 20:40:30 +00:00
Yegor Vialov
8a78745aa7 State change event log 2019-10-28 20:34:43 +00:00
Yegor Vialov
2a3eaabbe4 Remove some logs 2019-10-28 20:32:23 +00:00
Yegor Vialov
bcd175fbfb Fix service calls 2019-10-28 20:31:45 +00:00
Yegor Vialov
f9f013636d 710 2019-10-28 18:02:03 +00:00
Yegor Vialov
b34cc97080 Show entity page on main page 2019-10-28 17:59:47 +00:00
Yegor Vialov
327f623ef7 Remove HA url from main manu 2019-10-28 16:39:47 +00:00
Yegor Vialov
4d0877e5ae Remove Profile link 2019-10-28 16:36:20 +00:00
Yegor Vialov
0eac217399 Set minimum location update intervhals to 15 minute 2019-10-28 16:28:01 +00:00
Yegor Vialov
9c42ad687d 0.7.0 2019-10-28 10:43:10 +00:00
Yegor Vialov
5cda98da46 whats new key update 2019-10-24 19:48:06 +00:00
Yegor Vialov
958f545f65 Link to location tracking documentation 2019-10-24 19:41:58 +00:00
Yegor Vialov
44165993b4 Integration setting improvement 2019-10-24 19:18:27 +00:00
Yegor Vialov
283ae6cfd4 Integration settings improvements 2019-10-24 18:58:48 +00:00
Yegor Vialov
4068b295bd Battery level for device_tracker 2019-10-24 18:34:38 +00:00
Yegor Vialov
e36b33dcec Update README.md 2019-10-24 14:09:42 +03:00
Yegor Vialov
4b12912697 build 704 2019-10-23 18:24:10 +00:00
Yegor Vialov
49a21967cc Integration settings 2019-10-23 18:22:52 +00:00
Yegor Vialov
cf36406f2a Update README.md 2019-10-22 22:05:15 +03:00
Yegor Vialov
872ad044f1 Update README.md 2019-10-22 22:00:47 +03:00
Yegor Vialov
345301c03a Merge pull request #481 from estevez-dev/pre-release/702
Pre release/702
2019-10-22 21:57:43 +03:00
Yegor Vialov
117923413d Do not use static debug key for CI 2019-10-22 18:48:28 +00:00
Yegor Vialov
24ccbc58c4 703 2019-10-22 18:43:40 +00:00
Yegor Vialov
89c91b4b01 Location tracking improvements 2 2019-10-22 18:42:30 +00:00
Yegor Vialov
4494da1f4f 702 2019-10-21 18:26:12 +00:00
Yegor Vialov
c263542c54 Location tracking improvements 2019-10-21 18:22:25 +00:00
Yegor Vialov
c70f52a73d Merge pull request #480 from estevez-dev/pre-release/0.7.0
build 701
2019-10-21 00:35:22 +03:00
Yegor Vialov
423813d6fb Update README.md 2019-10-21 00:33:53 +03:00
estevez-dev
ec6a86f4b0 build 701 2019-10-21 00:13:34 +03:00
Yegor Vialov
64cf18cb23 Debug key 2019-10-20 20:04:53 +00:00
Yegor Vialov
e0e064bc67 build number 700 2019-10-20 18:28:07 +00:00
Yegor Vialov
5cee6cbd9c Remove auth code from logs 2019-10-20 18:22:51 +00:00
Yegor Vialov
43659b26f7 Fix workmanager init 2019-10-20 18:21:51 +00:00
estevez-dev
98e15ad429 Resolves #360 Update material design icons to version 4.5.95 2019-10-20 21:14:27 +03:00
estevez-dev
90728cdf8b WIP #260 Update MDI parser to 4.5.95 2019-10-20 21:02:57 +03:00
Yegor Vialov
d1ec4f36cc Resolves #49 Location tracking 2019-10-20 17:54:29 +00:00
Yegor Vialov
079070071e Remove webview plugin 2019-10-20 17:18:23 +00:00
Yegor Vialov
520fd6bc38 Migrate athentication from webview to deep linking 2019-10-20 16:45:44 +00:00
Yegor Vialov
085aead36b Add files via upload 2019-10-20 14:30:59 +03:00
Yegor Vialov
fcbaf298cc Delete google-services.json 2019-10-20 14:30:45 +03:00
Yegor Vialov
eedc0c9b22 More gitignore 2019-10-20 10:51:53 +00:00
Yegor Vialov
f70c1e12ff Update gitignore 2019-10-20 10:46:38 +00:00
Yegor Vialov
ec094a4362 Merge pull request #478 from estevez-dev/gitpod
Gitpod configuration
2019-10-18 21:14:11 +03:00
Yegor Vialov
11646c840e Merge branch 'master' into gitpod 2019-10-18 21:13:58 +03:00
Yegor Vialov
86987c57c9 Gitpod configuration 2019-10-18 18:11:06 +00:00
Yegor Vialov
e4d6e842f5 Gitpod configuration 2019-10-18 16:44:01 +00:00
estevez-dev
cfe4dd1c59 Vacuum state colors update 2019-10-16 19:39:14 +03:00
estevez-dev
3387ab2850 Resolves #416 Vacuum support 2019-10-16 19:34:29 +03:00
estevez-dev
abd23e27ea WIP #416 Vacuum support 2019-10-14 15:02:49 +03:00
estevez-dev
2f110b20bb Resolves #365 Fix missed group entities 2019-10-14 13:39:00 +03:00
estevez-dev
f88e6f9b61 Fix light controls inconsistance 2019-10-14 12:51:20 +03:00
estevez-dev
2836973dca Fix light controls issues 2019-10-09 20:45:46 +03:00
estevez-dev
a4477e9f83 Resolves #472 entity-filter fix 2019-09-30 21:21:16 +03:00
estevez-dev
96fa7ece25 Resolves #444 connection fix 2019-09-30 21:11:37 +03:00
estevez-dev
b84caa4cc3 Resolves #469 fix zero position when player paused 2019-09-30 21:05:14 +03:00
estevez-dev
49c212632e Fix app restore when on entity page 2019-09-30 20:58:04 +03:00
estevez-dev
92165aa7ed Remove intents 2019-09-30 20:30:52 +03:00
estevez-dev
cbbdb754aa Packages update 2019-09-26 22:46:52 +03:00
Yegor Vialov
7e3fe0608d Deep links native handler 2019-09-26 13:31:09 +03:00
Yegor Vialov
781f39f281 Deep linking manifest preparation 2019-09-26 13:24:27 +03:00
estevez-dev
bfb80f6f8c Resolves #457 Don't send media to unavailable players 2019-09-20 16:51:57 +03:00
estevez-dev
801b8f9288 Resolves #459 Send media to the same player 2019-09-20 16:47:02 +03:00
estevez-dev
b988fcfcdd Resolves #461 Hide media switch buttons if nothing playing 2019-09-20 16:42:50 +03:00
estevez-dev
dff6457cb2 Resolves #462 seek bar for idle players 2019-09-20 16:39:21 +03:00
estevez-dev
f50f68f318 Fix video with no duration 2019-09-20 16:15:26 +03:00
estevez-dev
c869ad41d9 Packadges update 2019-09-18 21:42:46 +03:00
estevez-dev
cd41f9a236 Fix 'Switch to' button 2019-09-18 21:41:02 +03:00
Yegor Vialov
1dbe162bf0 Merge pull request #467 from estevez-dev/release/0.6.7
Release/0.6.7
2019-09-18 21:35:55 +03:00
Yegor Vialov
1a52203bd7 Merge branch 'master' into release/0.6.7 2019-09-18 21:35:46 +03:00
estevez-dev
753df3c724 v.0.6.7 2019-09-18 21:18:24 +03:00
estevez-dev
dc62a08da3 Fix issue with unnamed view 2019-09-18 21:14:21 +03:00
estevez-dev
0c26aff498 672 2019-09-15 20:56:23 +03:00
estevez-dev
6323f8f2e6 Whats new url with app version 2019-09-15 20:55:28 +03:00
estevez-dev
885c0b1316 Fix stop player when switching to another 2019-09-15 20:27:49 +03:00
estevez-dev
14958d9165 Whats new page 2019-09-15 20:23:03 +03:00
estevez-dev
bf6a52e0b9 Improve media switching 2019-09-15 18:38:02 +03:00
estevez-dev
72aad5cc16 Turn off source player when swicthing media 2019-09-15 17:38:29 +03:00
estevez-dev
340e8569cc Switch media to another player 2019-09-15 17:29:49 +03:00
estevez-dev
8fc7d0b61e Fix entity state non updated on entity page 2019-09-15 14:34:00 +03:00
estevez-dev
5dcb27ada7 Fix no duration crash on media player 2019-09-15 11:10:19 +03:00
estevez-dev
db1a076132 Fix media popup menu 2019-09-15 10:48:35 +03:00
estevez-dev
6707201e23 Media player controls improvements 2019-09-15 10:39:08 +03:00
estevez-dev
b8b92171a8 Media player seek 2019-09-15 01:50:03 +03:00
estevez-dev
3dd7069292 Resolves #450 Quick access to active media players 2019-09-15 00:49:49 +03:00
estevez-dev
7177419472 Call service with POST instead of waiting for socket 2019-09-14 20:08:10 +03:00
estevez-dev
c37313cf07 Some refactoring 2019-09-14 19:53:39 +03:00
estevez-dev
a65f42d0fd Hide entity history and attributes under expandepble card 2019-09-14 19:37:52 +03:00
estevez-dev
78dd7df686 Entity class refactoring 2019-09-14 19:12:11 +03:00
estevez-dev
2ea7d9440c Entity class refactoring 2019-09-14 19:07:21 +03:00
estevez-dev
abdcd49368 Fix window resize crash on Chrome OS 2019-09-14 18:54:31 +03:00
estevez-dev
6da7a5ab90 Resolves #224 Main UI tablet support 2019-09-14 18:32:44 +03:00
estevez-dev
20ffe03139 View widget improvements 2019-09-14 12:31:50 +03:00
Yegor Vialov
a71213c589 Merge pull request #452 from estevez-dev/feature/tablet_ui
Feature/tablet ui
2019-09-14 12:20:13 +03:00
estevez-dev
d61103ac42 WIP #224 Dynamic multi column view 2019-09-14 12:18:37 +03:00
estevez-dev
298a64b7ae Packages update 2019-09-14 12:18:37 +03:00
Yegor Vialov
9e2c673966 Delete lovelace-card-implementation-request.md 2019-09-14 12:18:37 +03:00
Yegor Vialov
092469d668 Update issue templates 2019-09-14 12:18:37 +03:00
Yegor Vialov
bcf3dab0e2 Update issue templates 2019-09-14 12:18:37 +03:00
Yegor Vialov
7ecfc8a9ff Update issue templates 2019-09-14 12:18:37 +03:00
Yegor Vialov
ecf0a696f7 Create no-response.yml 2019-09-14 12:18:37 +03:00
estevez-dev
dc5db28e01 Packages update 2019-09-12 17:08:40 +03:00
Yegor Vialov
555f305c22 Delete lovelace-card-implementation-request.md 2019-09-12 14:07:24 +03:00
Yegor Vialov
76bf07cfcd Update issue templates 2019-09-12 14:06:44 +03:00
Yegor Vialov
c4663576d1 Update issue templates 2019-09-12 14:05:45 +03:00
Yegor Vialov
a64aa73aae Update issue templates 2019-09-12 14:00:16 +03:00
Yegor Vialov
a3a60dd707 Create no-response.yml 2019-09-12 13:36:37 +03:00
estevez-dev
9c28b0085b Tablet UI in progress 2019-09-10 15:40:49 +03:00
estevez-dev
d5baabdd53 Project structure changes 2019-09-09 18:50:35 +03:00
estevez-dev
56a333a852 v.0.6.6 2019-09-09 14:25:27 +03:00
estevez-dev
c5922368de Add whars new user message 2019-09-09 14:24:54 +03:00
estevez-dev
8c2316a51a Resolves #446 Fix conditional crads 2019-09-09 13:36:33 +03:00
estevez-dev
e2e6c015de Fix state color for paused media_player 2019-09-09 12:28:32 +03:00
estevez-dev
0a6ff4586d Share media url to HA CLient to play on media_player 2019-09-09 12:25:13 +03:00
estevez-dev
fc228d85ae Disable wip card 2019-09-08 19:06:41 +03:00
Yegor Vialov
61823cb43b Merge pull request #445 from estevez-dev/feature/light_card_support
WIP #212 Light card support
2019-09-08 19:05:18 +03:00
estevez-dev
127e0b8182 WIP #212 Light card support 2019-09-08 19:04:12 +03:00
estevez-dev
38c37fa212 Launch camera view in Chrome custom tab 2019-09-07 19:26:00 +03:00
estevez-dev
dfaf2a2924 Project structure change 2019-09-07 18:23:04 +03:00
estevez-dev
c90c40c046 Resolves #443 Lovelace view as panel support 2019-09-07 17:58:00 +03:00
estevez-dev
d2049b726a Resolves #348 Button entity refactoring 2019-09-07 17:27:23 +03:00
estevez-dev
6508f109f7 Minor gauge fixes 2019-09-07 17:04:40 +03:00
estevez-dev
37e63637a7 Resolves #348 Glance card improvements 2019-09-07 16:46:41 +03:00
estevez-dev
6650c5c145 Resolves #208 Gauge card 2019-09-07 15:47:09 +03:00
estevez-dev
9160dbf7f2 0.6.5 2019-09-05 15:43:13 +03:00
estevez-dev
243fcd7c49 Resolves #430 Trim any leading and trailing whitespace in address, port or token 2019-09-05 00:51:29 +03:00
estevez-dev
c114bcfb35 Resolves #426 Autofill port if not set 2019-09-05 00:48:20 +03:00
estevez-dev
83defb08f1 Resolves #425 Paste option for text fields 2019-09-05 00:43:45 +03:00
estevez-dev
57ebdbbe85 Main page in separate file 2019-09-05 00:25:03 +03:00
estevez-dev
c6aceed623 utils separated 2019-09-05 00:17:08 +03:00
estevez-dev
ba4c88ec5d Startup user messages fix 2019-09-05 00:09:40 +03:00
estevez-dev
ee1685e981 Refix #439 2019-09-04 23:42:19 +03:00
estevez-dev
996fbf7bba Login error handling improvements 2019-09-04 23:40:37 +03:00
estevez-dev
56cd8963d7 Fix mobile_app error help url 2019-09-04 23:13:11 +03:00
estevez-dev
5759aad0cb Remove unused plugin 2019-09-04 23:09:17 +03:00
estevez-dev
02717332f7 Revert all rash decisions 2019-09-04 22:46:14 +03:00
estevez-dev
8d1b159f56 User error messages 2019-09-04 22:03:52 +03:00
estevez-dev
fb335e1100 WIP: user messages 2019-09-04 15:10:25 +03:00
estevez-dev
5f0bc83d67 Resolves #439 Fix negative and zero columns issue for glance card 2019-09-03 23:58:28 +03:00
estevez-dev
6a8cee2cc2 Login errors handling improvements 2019-09-03 23:44:03 +03:00
estevez-dev
0d2f1cf9aa No mobile_app error handling 2019-09-03 23:35:33 +03:00
estevez-dev
8efeb3da8a Error messages refactored 2019-09-03 23:25:39 +03:00
estevez-dev
620aa3b8d8 Disabling location tracking 2019-09-03 20:13:23 +03:00
estevez-dev
ab5bf3b807 WIP #49 Catch http errors inside location isolate 2019-09-02 21:21:38 +03:00
estevez-dev
6663bcad72 Resolves #339 Authenticated webview for panels and config sections 2019-09-02 21:08:20 +03:00
estevez-dev
113cd29f74 Resolves #431 Add default card type if not specified 2019-09-02 19:36:20 +03:00
estevez-dev
f2fdfb0a32 WIP #339 Open unsupported Panels as authenticared webviews 2019-09-02 19:05:49 +03:00
estevez-dev
691e48a36b 0.6.5-alpha2 2019-09-01 23:20:29 +03:00
estevez-dev
2036cc117f Fix support app popup buttons 2019-09-01 23:19:26 +03:00
estevez-dev
389d28a1e1 Location manager optimizations 2019-09-01 23:12:43 +03:00
estevez-dev
27e6198d83 Remove test data send 2019-09-01 22:31:46 +03:00
estevez-dev
de762a4878 Resolves #401 Fix login restart and several login views opened 2019-09-01 22:27:56 +03:00
estevez-dev
e8efefe25d Remove test data send 2019-09-01 22:01:27 +03:00
estevez-dev
21f3e8985a 0.6.5-alpha1 2019-09-01 00:30:27 +03:00
estevez-dev
622543d405 Fix notification handlers return type 2019-09-01 00:14:54 +03:00
estevez-dev
abdc0fc1c8 Remove dead code 2019-09-01 00:12:16 +03:00
estevez-dev
1ecb839042 Add user messages 2019-08-31 23:55:32 +03:00
estevez-dev
cece4d1e16 Add location tracking switch disabled by default 2019-08-31 23:09:30 +03:00
estevez-dev
623634cb6e Send location on app resume 2019-08-31 22:37:55 +03:00
estevez-dev
f9c37f5084 Refactor ConnectionManager and DeviceInfoManager 2019-08-31 22:10:07 +03:00
estevez-dev
3e12f4f8a4 Create MobileAppIntegrationManager 2019-08-31 22:06:52 +03:00
estevez-dev
b07ff6fe71 WIP #49 Add location update interval settings 2019-08-30 21:45:34 +03:00
estevez-dev
5a3b57c28e Update location permissions 2019-08-30 19:56:22 +03:00
Yegor Vialov
e858eee83b Merge pull request #396 from koying/history_tweak
History tweaks
2019-08-30 19:48:04 +03:00
Yegor Vialov
73f00d3bd7 Merge pull request #428 from estevez-dev/gelocator_plugin
WIP #49 - geolocator plugin testing
2019-08-30 19:46:25 +03:00
estevez-dev
eea59cf11b WIP #49 - geolocator plugin testing 2019-08-30 16:12:03 +03:00
estevez-dev
61b459ed8a WIP #49 2019-08-30 15:51:39 +03:00
estevez-dev
dca8c309aa WIP #49 Location tracking service and test alarm manager 2019-08-30 15:04:51 +03:00
estevez-dev
be53500104 WIP #49 2019-08-29 12:57:24 +03:00
estevez-dev
bc1a791608 WIP #49 2019-08-28 19:23:24 +03:00
Yegor Vialov
b112ff980a Update README.md 2019-08-28 00:08:17 +03:00
estevez-dev
7beab9ae93 0.6.4 2019-08-27 20:06:18 +03:00
estevez-dev
8c0d1f90a3 Resolves #422 Fix noSuchMethod for group-based UI 2019-08-27 20:05:33 +03:00
Yegor Vialov
05c05ba768 Possibly resolves #422 2019-08-27 12:05:33 +03:00
estevez-dev
67e885e76a Update subscribtions 2019-08-26 19:42:26 +03:00
estevez-dev
594bce0b8d Update packages 2019-08-26 19:00:17 +03:00
estevez-dev
7f6569e0db 0.6.3 2019-08-26 18:56:06 +03:00
estevez-dev
1c829c8364 Remove sensitive information from log 2019-08-26 18:55:12 +03:00
estevez-dev
7ca4b02e6d Resolves#413 Handling removed mobile_app integration 2019-08-26 18:30:49 +03:00
estevez-dev
fadfefd836 Resolves #421 Manual long-lived token 2019-08-26 18:04:40 +03:00
estevez-dev
37155901ef Resolves #205, Resolves #417 Condotional cards support 2019-08-26 17:03:28 +03:00
estevez-dev
fbbb96409d Project structure change in progress 2019-08-24 21:22:32 +03:00
estevez-dev
5126c54914 Resolves #412 Add 'Support app development' option 2019-08-24 20:39:25 +03:00
estevez-dev
916d0b7e3c WIP #412 2019-08-24 15:22:23 +03:00
estevez-dev
0815840a9c WIP #412 2019-08-23 14:13:58 +03:00
estevez-dev
bc237796b2 0.6.2 2019-08-21 21:30:57 +03:00
estevez-dev
7f44800f64 Fix mobile_app registration and update 2019-08-21 21:30:11 +03:00
estevez-dev
85ac746e9d 0.6.1 2019-08-21 18:49:56 +03:00
estevez-dev
8215175098 Resolves #409 Remove slesh from the end of HA address 2019-08-21 18:48:53 +03:00
estevez-dev
39ee8b1799 Resolves #410 os_version removed from mobile_app registration 2019-08-21 18:40:16 +03:00
estevez-dev
c76d3d68c8 Resolves #406 Minimum light brightness is set to 1 isntead of 0 2019-08-21 18:28:07 +03:00
estevez-dev
cde257922b 0.6.0 2019-08-21 09:40:18 +03:00
estevez-dev
be0c9d3372 App bundle test 2019-08-20 11:22:47 +03:00
estevez-dev
66cd7ea307 0.6.0-alpha3 2019-08-16 15:05:43 +03:00
estevez-dev
b704ce6984 0.6.0-alpha3 2019-08-16 14:01:10 +03:00
estevez-dev
247c856a41 Resolves #397 Add default icon for device_tracker 2019-08-16 13:44:29 +03:00
estevez-dev
9afaebfa12 Resolves #401 Climate support fixes 2019-08-16 13:29:41 +03:00
estevez-dev
929abea5d3 Login and mobile app registration improvements 2019-08-16 12:32:36 +03:00
Chris "koying" Browet
13102a6b04 CHG: [history] wrap around 2019-06-27 18:25:36 +02:00
Chris "koying" Browet
57c3083f9f CHG: [history widget] select last measurement initally 2019-06-27 18:25:24 +02:00
estevez-dev
5c31ddd00f Resolves #345 Add default icon for Remote 2019-06-23 16:19:28 +03:00
estevez-dev
8f55be187d Resolves #324 devider fix, entity card padding fix 2019-06-23 16:08:12 +03:00
estevez-dev
1fe82d8b0d Resolves #334 Fix plug device_class icons 2019-06-23 15:27:55 +03:00
estevez-dev
cbc56a8105 Resolves #336 Replace 'unknown' state with '-'. Show displayState for badges 2019-06-23 15:24:08 +03:00
estevez-dev
b63cddfa46 Resolves #330 Add Help menu item 2019-06-23 15:15:33 +03:00
estevez-dev
91db82f730 Resolves #331 Menu item text change 2019-06-23 15:11:04 +03:00
estevez-dev
0c4d1b78ff Resolves #323 fix widget padding for entity page 2019-06-23 15:09:18 +03:00
estevez-dev
5af2fd0562 Resolves #376 Dynamic font size on badges 2019-06-23 14:53:11 +03:00
estevez-dev
2375543ebf Fix camera stream open 2019-06-23 14:36:15 +03:00
estevez-dev
de187f3ed5 Update mdi array builder script 2019-06-21 21:30:26 +03:00
estevez-dev
9266ffacf3 Update Material Design Icons font to 3.6.95 2019-06-21 21:28:37 +03:00
estevez-dev
3c0ca5d16d Resolve #382 VIew camera in chrome custom tab 2019-06-21 21:01:53 +03:00
estevez-dev
caabf25260 WIP #382 Open camera stream in CHrome custom tab 2019-06-21 14:29:56 +03:00
estevez-dev
0af2afbb80 Add links to web version of COnfiguration secrtions 2019-06-21 13:33:28 +03:00
estevez-dev
12d226509d App registration improvements 2019-06-21 13:21:30 +03:00
estevez-dev
3417c38426 Resolves #386 2019-06-21 12:53:03 +03:00
estevez-dev
c7fc5afbb8 Resolves #389 Improve app registration checking 2019-06-21 12:39:58 +03:00
estevez-dev
11f565a9dc Resolves #388 2019-06-21 12:05:55 +03:00
estevez-dev
53240faac3 Fix automatic OAuth window open issue 2019-06-16 22:57:50 +03:00
estevez-dev
95d4878785 Resolves #48 Native notifications 2019-06-16 20:08:50 +03:00
estevez-dev
ef15026203 Fix authentication process. App register in background 2019-06-16 16:32:55 +03:00
estevez-dev
ad6355503b WIP #48 Show dialog on app registration 2019-06-16 00:23:11 +03:00
estevez-dev
491c2b0dc0 WIP #48 Notifications with mobile_app component 2019-06-16 00:08:13 +03:00
estevez-dev
5b99ade088 Resolves #318 add mobile_app integration 2019-06-15 18:07:11 +03:00
estevez-dev
e1d9d9f304 Stop connection init if settings is empty 2019-06-15 14:36:11 +03:00
estevez-dev
209ccd4f7f New error class 2019-04-19 21:43:52 +03:00
estevez-dev
5a8a207f2e minor fix 2019-04-19 14:40:05 +03:00
estevez-dev
19c85d9c16 Don't handle state change if fetch is in progress 2019-04-19 14:38:02 +03:00
estevez-dev
a916ddfa50 Resolves #364, Resolves #363 Connection issues 2019-04-19 14:07:44 +03:00
estevez-dev
8c1ad9c7f9 Fix login button 2019-04-05 14:07:03 +03:00
estevez-dev
93af1eca7e Resolves #355 Add login button on empty screen 2019-04-05 13:39:54 +03:00
estevez-dev
cabf836fa3 WIP #355 Disconnect when logout 2019-04-05 13:06:14 +03:00
estevez-dev
15b3d31a6f Resolves #353 Show error if connection drops 2019-04-05 12:23:31 +03:00
estevez-dev
9b98689012 Fix connection error handling 2019-04-05 12:08:32 +03:00
estevez-dev
84ebd0c33c Resolves #352 Fix panels clear after logout 2019-04-05 11:59:13 +03:00
estevez-dev
ccd7774931 Resolves #350 Fix displayed hostname 2019-04-05 11:57:58 +03:00
estevez-dev
b2773635f5 Connection improvements 2019-04-05 11:48:41 +03:00
estevez-dev
8b046b7313 Merge branch '0.6.0-alpha1-1' 2019-04-04 22:25:19 +03:00
estevez-dev
885a516676 alpha2 2019-04-04 22:12:08 +03:00
estevez-dev
921b0e09b0 Merge branch 'terms_and_privacy' into 0.6.0-alpha1-1 2019-04-04 22:10:29 +03:00
estevez-dev
277c67fc6f Add padding for links in About dialog 2019-04-04 21:54:41 +03:00
estevez-dev
2a01ff8a03 Bump version in UI 2019-04-04 21:51:05 +03:00
estevez-dev
b246b7bc1d 0.5.3 and new build numbers 2019-04-04 21:44:16 +03:00
estevez-dev
e1868b9a14 Add privacy polici and terms and conditions links 2019-04-04 21:43:23 +03:00
estevez-dev
125f3ac16c Resolves #327 Timer duration parsing error 2019-04-04 21:38:23 +03:00
estevez-dev
be502b5668 Discord icon fix 2019-04-04 21:38:05 +03:00
estevez-dev
6f33fdca9f New app icon 2019-04-04 21:37:41 +03:00
estevez-dev
a7cda2a35e WIP #48 Notifications 2019-03-30 00:29:52 +02:00
estevez-dev
102b10ade0 WIP #48 Notifications 2019-03-29 13:09:34 +02:00
estevez-dev
4e96b9adbb Build 101 2019-03-29 11:16:04 +02:00
estevez-dev
b9581d3762 Resolves #347, Resolves #346 Connection and reconnection 2019-03-29 11:04:43 +02:00
estevez-dev
7c010359c3 Resolves #340 Connection refactoring 2019-03-26 00:18:30 +02:00
estevez-dev
4a75243994 WIP #340 Refactor getting data and error handling 2019-03-22 14:04:20 +02:00
estevez-dev
d29d7e5b3b WIP #340 2019-03-21 16:55:25 +02:00
estevez-dev
5ebd25e0d1 Resolves #59 Storing token in secure storage 2019-03-21 14:25:05 +02:00
estevez-dev
b7d5a53e86 Resolves #341 Add logout 2019-03-21 14:08:07 +02:00
estevez-dev
20d3498bfd WIP #341 Logout 2019-03-20 23:38:57 +02:00
estevez-dev
67d7bb45f5 Resolves #338 OAuth with Home Assistant 2019-03-20 23:05:25 +02:00
estevez-dev
6a03105d01 WIP 2019-03-20 19:01:30 +02:00
estevez-dev
5ae580ecf1 Chachesd HomeAssistance instance for every view in app 2019-03-20 12:48:00 +02:00
estevez-dev
0efef33e53 Fix CleartextTraffic issue. WIP #338 2019-03-19 23:20:57 +02:00
estevez-dev
ccb88884a7 Settings loading refactored. WIP #338 2019-03-19 23:07:40 +02:00
estevez-dev
d70ba0a55a WIP #48 2019-03-18 23:37:45 +02:00
estevez-dev
5140840d3a Resolves #327 Timer duration parsing error 2019-03-14 16:39:37 +02:00
estevez-dev
14759fd3c9 Discord icon fix 2019-03-14 14:35:30 +02:00
estevez-dev
fed35be517 New app icon 2019-03-14 14:07:36 +02:00
estevez-dev
db77cc43aa Version 0.5.0 2019-03-13 22:42:03 +02:00
estevez-dev
b2269cc96d Resolves #293 Fix updater icon 2019-03-13 22:40:54 +02:00
estevez-dev
8b28bb2e9e Resolves #314 card icon priority 2019-03-13 22:12:01 +02:00
estevez-dev
fb456878bc Resolves #258 Timer support 2019-03-13 21:33:58 +02:00
estevez-dev
8b961ebd69 Resolves #83 Calendar support 2019-03-13 20:07:44 +02:00
estevez-dev
9bd3a41cf5 Resolves #140 Scenes 2019-03-13 18:06:43 +02:00
estevez-dev
491ae55a2a Resolves #299, Resolves #234 Fix entity picture url issue 2019-03-13 17:48:49 +02:00
estevez-dev
e1d2981782 Add 'Open Web UI' menu link 2019-03-13 17:25:08 +02:00
estevez-dev
74572168ae Resolves #116 Add Iframe panel support 2019-03-13 17:23:23 +02:00
estevez-dev
92d0b5c055 Migrate to AndroidX 2019-03-13 17:05:15 +02:00
estevez-dev
3504d3276c Resolves #11 Add Panels fetching 2019-03-13 16:39:23 +02:00
estevez-dev
736b38b64c Some UI improvements for #245 2019-03-13 14:08:54 +02:00
estevez-dev
cb118b599a Resolves #245 Add special row elements support for entities card 2019-03-13 00:56:57 +02:00
estevez-dev
a08a056cff Resolves #254 Missed entities 2019-03-12 23:35:33 +02:00
estevez-dev
0ef2ebfe31 Fix 'Paste color' button background when saved color is null 2019-03-10 23:49:05 +02:00
estevez-dev
4f4ac3b574 Resolves #310 Add assumed state for locks 2019-03-10 23:41:14 +02:00
estevez-dev
7064cb0e30 Resolves #272 Add 'Copy color' and 'Past color' 2019-03-10 23:28:23 +02:00
estevez-dev
91a99e17e0 Resolves #320 Fix eEntity_picture size 2019-03-10 22:50:39 +02:00
estevez-dev
2e9b7d20b9 Fix broken icons 2019-03-10 19:28:11 +02:00
estevez-dev
b8aa808de4 Update Material Design Icons to 3.5.95 2019-03-09 13:26:45 +00:00
estevez-dev
2cfa92a42b Reverts #308 2019-03-06 16:50:30 +00:00
estevez-dev
146efef72d Gradle config for Chrome OS build 2019-03-06 16:42:05 +00:00
estevez-dev
8c9804e16f WIP #308 2019-03-02 20:13:24 +02:00
estevez-dev
a4736bfb5a Message handling improvements 2019-03-02 18:00:25 +02:00
Yegor Vialov
15c54df629 Update README.md 2019-02-26 11:31:39 +02:00
Yegor Vialov
32ffef21e9 Update README.md 2019-02-26 11:31:08 +02:00
Yegor Vialov
848d3cb510 Update README.md 2019-02-26 10:45:25 +02:00
Yegor Vialov
8a4caeebba Update README.md 2019-02-26 10:43:47 +02:00
Yegor Vialov
aa923f0fba Update README.md 2019-02-26 10:39:09 +02:00
Yegor Vialov
4d8f50ddd5 Update README.md 2019-02-26 10:33:34 +02:00
Yegor Vialov
fe06b21a6c Update README.md 2019-02-26 10:30:08 +02:00
Yegor Vialov
efed7fb1b5 Update README.md 2019-02-26 10:23:03 +02:00
estevez-dev
df2cbb7d13 Resolves #313 Fix missed mute button for media_player 2019-02-22 15:39:53 +02:00
estevez-dev
03edaa9ca2 Resolves #168 Fix error when entity view closed before history loaded 2019-02-22 15:33:10 +02:00
estevez-dev
1a7457abf9 Resolves #311 Rebuild tabs only if views count changes 2019-02-22 15:28:11 +02:00
estevez-dev
00889b13e0 Resolves #312 Add white value control for light 2019-02-22 15:15:27 +02:00
estevez-dev
0615073ec4 Get color from rgb_color if there is no hsv_color attribute 2019-02-22 14:20:01 +02:00
estevez-dev
eb7d17d147 WIP #308 Move entity icon generation into EntityIcon widget 2019-02-21 16:32:55 +02:00
estevez-dev
24f80feeee Resolves #187 Fix crash on view count changes 2019-02-21 15:35:58 +02:00
estevez-dev
4b6dda5a9c version 0.4.4 2019-02-20 18:54:54 +02:00
estevez-dev
4099fa0c83 WIP #302 fix SVG size 2019-02-20 18:50:58 +02:00
estevez-dev
76057e8797 WIP #302 simple SVG support 2019-02-20 17:55:56 +02:00
estevez-dev
538d3603dc Resolves #306 Improve camera connection 2019-02-20 16:39:57 +02:00
estevez-dev
bc0e72ca52 version 0.4.3 2019-02-20 13:58:30 +02:00
estevez-dev
f25a47beb2 Add camera stream reconnect on closing 2019-02-20 13:57:25 +02:00
estevez-dev
cc3c6b0087 Resolves #307 Support different frame bounderies for MJPEG stream 2019-02-20 12:06:03 +02:00
estevez-dev
6cf80c0bfd version 0.4.2 2019-02-19 19:22:40 +02:00
estevez-dev
8ce9bdb7a5 Resolves #303, Resolves #304 Fix wrong camera stream url 2019-02-19 19:21:52 +02:00
Yegor Vialov
31e50150b1 Resolves #263 Fix error when supported_features is null 2019-02-17 13:52:24 +02:00
Yegor Vialov
e359150d97 Version 0.4.0 2019-02-16 22:05:43 +02:00
Yegor Vialov
93680c981c Resolves #283 Add possibility to restrat HA 2019-02-16 22:04:49 +02:00
Yegor Vialov
e06b66c523 Resolves #259 target_temp_step support for climate 2019-02-16 20:44:41 +02:00
Yegor Vialov
3dea844e1e Resolves #290 Hide pin inputs if code_format is null 2019-02-16 20:33:56 +02:00
Yegor Vialov
62b1af30e0 Resolves #291 some padding issues 2019-02-16 19:59:39 +02:00
Yegor Vialov
e006c4e403 build number 2019-02-10 22:36:30 +02:00
Yegor Vialov
983573388e Remove pull-to-refresh. Add new menu in header. 2019-02-10 22:33:46 +02:00
Yegor Vialov
bdd1dc7e17 Hide light additional controls if state=unavailable 2019-02-10 19:06:07 +02:00
Yegor Vialov
9c1970ee14 build number 2019-02-10 18:26:48 +02:00
Yegor Vialov
d0e0bf3571 Current color for color picker fix 2019-02-10 18:24:54 +02:00
Yegor Vialov
b399357517 Back to old version format 2019-02-10 17:23:57 +02:00
Yegor Vialov
0290cd3a32 Resolves #273 New color picker 2019-02-10 17:15:52 +02:00
Yegor Vialov
d8a1d03179 v.3.14.87 2019-02-09 02:21:20 +02:00
Yegor Vialov
216fad3cb9 Fix entity page padding 2019-02-09 02:21:20 +02:00
Yegor Vialov
fead6ea348 Resolves #143 Camera support 2019-02-09 02:21:20 +02:00
Yegor Vialov
8814687be6 WIP #143 Initial not optimized MJPEG streaming 2019-02-09 02:21:20 +02:00
Yegor Vialov
71c0e2caa0 Change version numeration 2019-02-09 02:21:20 +02:00
Yegor Vialov
1531c41542 Create CODE_OF_CONDUCT.md 2019-02-01 12:55:25 +02:00
Yegor Vialov
bc90d013e8 Build 86 2019-02-01 11:52:09 +02:00
Yegor Vialov
2adfaca0c4 Resolves #286 2019-02-01 11:51:35 +02:00
Yegor Vialov
6cc1a37d9d Resolves #285 2019-02-01 11:49:27 +02:00
Yegor Vialov
4bb616b327 Build number 2019-01-31 21:14:20 +02:00
Yegor Vialov
38219618ba version 0.3.14 2019-01-31 21:05:11 +02:00
Yegor Vialov
6774b53758 Disable unfinished camera support 2019-01-31 20:57:33 +02:00
Yegor Vialov
29a94c882f WIP MJPEG stream handling 2019-01-31 01:04:13 +02:00
Yegor Vialov
5897fa3a99 WIP #143 2019-01-30 00:25:41 +02:00
Yegor Vialov
7af92c2dc9 WIP #143 Camera support 2019-01-29 22:03:08 +02:00
estevez-dev
1094177a42 Resolves #282 Trigger button for alarm 2019-01-29 18:51:28 +02:00
estevez-dev
5e814e8109 Resolves #204 Alarm panel card support 2019-01-29 15:00:15 +02:00
estevez-dev
24c7675fa4 Resolves #142 Alarm control panel support 2019-01-29 11:54:26 +02:00
Yegor Vyalov
dc3ca38c78 WIP #142 Alarm control panel 2019-01-28 16:48:49 +02:00
Yegor Vialov
96b528e055 Update README.md 2019-01-28 15:05:19 +02:00
Yegor Vialov
3858036631 Resolves #139 Trigger for automations 2019-01-25 23:48:31 +02:00
Yegor Vialov
19d42ceeb3 Fix names null 2019-01-25 23:30:23 +02:00
Yegor Vialov
a2836a3603 Resolves #257 2019-01-25 23:08:12 +02:00
Yegor Vialov
2a45758a6d Resolves #268 Badges for Lovelace UI 2019-01-25 22:55:41 +02:00
Yegor Vialov
dc1bf4d878 WIP #266 fix icons 2019-01-25 22:45:54 +02:00
Yegor Vialov
e82ba60c4e WIP #266 Card parsing proper error handling and toString for some fields 2019-01-25 22:41:26 +02:00
Yegor Vialov
09199d30e8 Resolves #274 Use Lovelace UI by default 2019-01-25 22:29:16 +02:00
Yegor Vialov
724d32dbe2 Resolves #277 Remove legacy password support 2019-01-25 22:27:13 +02:00
Yegor Vialov
949c8ee44e Resolves #264 Throttle for sending thermostat temperature 2019-01-25 22:19:11 +02:00
Yegor Vialov
1a446d34c7 Resolves #262 Cler 2019-01-25 22:19:11 +02:00
Yegor Vyalov
22a5847285 Resolves #270 Current light effect for lights 2019-01-24 12:26:38 +02:00
Yegor Vialov
1c8f770f10 Resolves #121 Markdown card support 2019-01-23 23:34:45 +02:00
Yegor Vialov
be5ea55f6b Fix light color controls appearence issue 2018-12-29 17:44:11 +02:00
Yegor Vialov
c65ade9827 Fix icons for entity button 2018-12-25 11:48:37 +02:00
Yegor Vialov
d3c1422b9e Version 0.3.13 2018-12-15 14:37:55 +02:00
Yegor Vialov
b6ac9f985f Entity state by device class 2018-12-15 14:37:00 +02:00
Yegor Vialov
a59de4b6dc Resolves #255 Refresh UI for newly appeared entity 2018-12-15 14:09:37 +02:00
Yegor Vialov
f507d5df0c Resolves #242 2018-12-14 19:37:49 +02:00
Yegor Vialov
f77e46de37 Version 0.3.12 2018-12-14 17:04:52 +02:00
Yegor Vialov
cda17b1217 Resolves #232 2018-12-14 17:03:18 +02:00
Yegor Vialov
be560769ef Resolves #243 2018-12-14 16:57:11 +02:00
Yegor Vialov
3815800e32 Resolves #253 removing trash characters from state string 2018-12-14 16:48:35 +02:00
Yegor Vialov
a3226311a2 Fix default tap actions 2018-12-14 16:31:41 +02:00
Yegor Vialov
79669243c2 Remove some logging 2018-12-14 16:01:13 +02:00
Yegor Vialov
fdc81f6ea4 Resolves #237 2018-12-14 15:59:47 +02:00
Yegor Vialov
7fe44459e7 Resolves #252, Resolves #249 2018-12-14 14:28:23 +02:00
Yegor Vialov
a8500d44e1 Version 0.3.11 2018-12-13 23:42:34 +02:00
Yegor Vialov
b4d4c5abec Relates to #248 old format support 2018-12-13 23:40:20 +02:00
Yegor Vialov
c19a3f272a Resolves #248 tap_action parsing fix 2018-12-13 23:37:54 +02:00
Yegor Vialov
b264534858 Add files via upload 2018-12-12 10:43:26 +02:00
Yegor Vialov
ab53f77f9e Add files via upload 2018-12-12 10:40:50 +02:00
Yegor Vialov
c73956720c Create empty 2018-12-12 10:39:42 +02:00
Yegor Vialov
051041e794 build number 2018-12-07 23:00:20 +02:00
Yegor Vialov
5c83be9fee Resolves #207 Entity filter card support 2018-12-07 22:04:14 +02:00
Yegor Vialov
4bece42693 build number 2018-11-29 21:46:47 +02:00
Yegor Vialov
4ae107fe4c Resolves #230 Vertical stack card 2018-11-29 21:45:46 +02:00
Yegor Vialov
9523ed2562 Build number 2018-11-25 20:45:22 +02:00
Yegor Vialov
9c403480e2 Resolves #120 Horizontal Stack Cards 2018-11-25 20:44:19 +02:00
Yegor Vialov
20b1b90e39 Resolves #206 Entity button with tap and hold events 2018-11-25 18:09:06 +02:00
Yegor Vialov
5633e30448 WIP #206 Entity button card 2018-11-25 17:33:33 +02:00
Yegor Vialov
4492fb9f0c Build number 2018-11-24 17:30:02 +02:00
Yegor Vialov
36410752e4 Fix app version 2018-11-24 17:29:33 +02:00
Yegor Vialov
0219f7bfbb Version 0.3.10 2018-11-24 17:04:41 +02:00
Yegor Vialov
5f3c77f4b9 Resolves #145 Fan support 2018-11-24 17:00:45 +02:00
Yegor Vialov
a36c7a9ca3 Resolves #186 Switch for group with same domain antities 2018-11-24 11:33:59 +02:00
Yegor Vialov
56ce6dfeeb Improved tap animation for glance card 2018-11-24 11:02:28 +02:00
Yegor Vialov
67c214454f build number 2018-11-24 01:20:18 +02:00
Yegor Vialov
73398378c4 Resolves #227 2018-11-24 00:37:55 +02:00
Yegor Vialov
215871ce9e Resolves #226 2018-11-23 21:59:33 +02:00
Yegor Vialov
fd8ea6befd Show auto groups that is not hidden 2018-11-23 19:30:16 +02:00
Yegor Vialov
809a1a1c8c Resolves #146 Lock support 2018-11-23 19:18:17 +02:00
Yegor Vialov
fc8f2f200f Small screens support 2018-11-23 18:16:38 +02:00
Yegor Vialov
f41c9f9197 Resolves #202 Service call info 2018-11-23 16:38:26 +02:00
Yegor Vialov
cdf55ce68b Resolves #201 New progress indicator in the bottom of the app 2018-11-23 16:30:42 +02:00
Yegor Vialov
12088d9516 Resolves #223, Resolves #197 2018-11-23 16:03:38 +02:00
Yegor Vialov
a0235ee385 Handle entity taps and holds in one place 2018-11-23 15:06:42 +02:00
Yegor Vialov
67fbdb13c6 Proper autogenerateg groups detection 2018-11-23 14:33:03 +02:00
Yegor Vialov
c5960de0be Resolves #193 Source selection support for media_player 2018-11-23 14:18:25 +02:00
Yegor Vialov
da15e880ec Sound mode support for media player 2018-11-23 14:11:34 +02:00
Yegor Vialov
efbe33f4e3 Fix font sizes, long entity states 2018-11-18 16:40:12 +02:00
Yegor Vialov
af84c99a2d build 69 2018-11-18 13:25:00 +02:00
Yegor Vialov
438449cad8 Handling taps on entity name and state for glance card 2018-11-18 13:24:05 +02:00
Yegor Vialov
d9ca55c3b7 Resolves #131 2018-11-18 13:19:00 +02:00
Yegor Vialov
f248268984 New bottom info bar 2018-11-18 12:46:54 +02:00
Yegor Vialov
8ee096595c Resolves #192 Don't fetch data on every app resume 2018-11-18 09:47:22 +02:00
Yegor Vialov
a8e79c289b Version 0.3.9 2018-11-17 23:07:58 +02:00
Yegor Vialov
2cd8533882 Link in about box 2018-11-17 23:02:05 +02:00
Yegor Vialov
0a21d9c690 Fix repository url 2018-11-17 22:55:04 +02:00
Yegor Vialov
e77bb533b1 Fix non-lovelace UI cards creating issue 2018-11-17 22:52:06 +02:00
Yegor Vialov
96f1211395 Resolves #55 2018-11-17 22:40:33 +02:00
Yegor Vialov
1e4cb03470 build number 66 2018-11-16 23:36:23 +02:00
Yegor Vialov
ab67b557ca Resolves #183 Service call support for glance card 2018-11-16 23:35:08 +02:00
Yegor Vialov
82c9bd26d1 WIP #183 tap_action support. State change event fix 2018-11-16 22:32:43 +02:00
Yegor Vialov
1bd04abd37 Resolves #189 2018-11-16 14:30:43 +02:00
Yegor Vialov
c5942d22b3 WIP #183 Custom names and icons 2018-11-15 19:08:47 +02:00
Yegor Vialov
37ad5e81cf WIP #183 Glance card ui improvements 2018-11-15 16:13:54 +02:00
Yegor Vialov
26187e6233 Resolves #155 2018-11-15 15:57:17 +02:00
Yegor Vialov
b8f6fda8d3 Remove assumedState from Entity class 2018-11-15 12:49:46 +02:00
Yegor Vialov
62b4e99810 build number 2018-11-14 19:53:00 +02:00
Yegor Vialov
25bf10a64e WIP #183 2018-11-14 19:52:17 +02:00
Yegor Vialov
874410964d Resolves #178 2018-11-14 18:03:50 +02:00
Yegor Vialov
57c30917b3 WIP #55 main media controls 2018-11-14 15:43:04 +02:00
Yegor Vialov
87f89b63e1 State const 2018-11-14 15:14:46 +02:00
Yegor Vialov
3190b45db3 Resolves #176 History can be requested only once per 30 seconds 2018-11-14 13:38:02 +02:00
Yegor Vialov
f5434e26e5 Resolves #174 2018-11-14 13:14:45 +02:00
Yegor Vialov
86b6ad6bba Resolves #171 2018-11-14 12:35:08 +02:00
Yegor Vialov
8a9641fbed Internal build 63 2018-11-12 20:54:48 +02:00
Yegor Vialov
5142391da2 Resolves #184 2018-11-12 20:47:49 +02:00
Yegor Vialov
01090dc3b1 Some improvements 2018-11-12 20:47:49 +02:00
Yegor Vialov
0a7bbb5a38 Update README.md 2018-11-12 16:53:26 +02:00
Yegor Vialov
c347eee9f0 Update README.md 2018-11-12 16:52:10 +02:00
Yegor Vialov
90f197ba54 Update README.md 2018-11-12 12:41:01 +02:00
Yegor Vialov
e09917c687 Merge pull request #177 from estevez-dev/add-license-1
Create LICENSE
2018-11-12 10:35:29 +02:00
Yegor Vialov
a69da832cb Create LICENSE 2018-11-12 10:35:17 +02:00
Yegor Vialov
c1708fd980 WIP #55 Fix preview 2018-11-11 22:21:29 +02:00
Yegor Vialov
c85a9bbe27 WIP #55 2018-11-11 20:54:54 +02:00
Yegor Vialov
d9790dedbb Cards creating optimization 2018-11-11 19:51:02 +02:00
Yegor Vialov
30e4eaa023 Move history widget under additional controls 2018-11-11 18:49:04 +02:00
Yegor Vialov
54e00c3403 WIP #55 2018-11-11 18:36:49 +02:00
Yegor Vialov
0e3474bbcb version 0.3.8 2018-11-06 21:12:24 +02:00
Yegor Vialov
efd06ca547 Resolves #164
Template cover has supported_features = 11, but it supports setting position.
2018-11-06 14:10:34 +02:00
Yegor Vialov
69fd37d4fe Fix settings saving issue 2018-11-06 14:01:00 +02:00
Yegor Vialov
4a49372410 version code 2018-11-05 20:41:19 +02:00
Yegor Vialov
478f58e2d8 v.0.3.7 hotfixes 2018-11-05 20:37:35 +02:00
Yegor Vialov
a87aff67ac Resolves #170 Saving settings button issue fix 2018-11-05 20:34:56 +02:00
Yegor Vialov
644f5e7fc6 Resolves #166 2018-11-05 20:21:44 +02:00
Yegor Vialov
3cddac3dc6 Resolves #167 2018-11-05 20:15:20 +02:00
Yegor Vialov
ab30c64eab v.0.3.7 2018-11-04 23:20:58 +02:00
Yegor Vialov
6d79487219 Resolves #165 Hide controls for unavailable lights 2018-11-04 23:13:25 +02:00
Yegor Vialov
9f7444eae0 Remove widgetHeigth 2018-11-04 22:57:53 +02:00
Yegor Vialov
788d682f2f Resolves #160 Flexible entity heigth 2018-11-04 22:55:09 +02:00
Yegor Vialov
66f84952f0 Get entity name from entity id if was not set 2018-11-04 22:25:22 +02:00
Yegor Vialov
5d95c3702d Resolves #162 View names display 2018-11-04 22:19:45 +02:00
Yegor Vialov
1f0bd8059b Resolves #164 Allow to open cover if it is not fully opened 2018-11-04 21:55:37 +02:00
Yegor Vialov
a7830df628 Fix covers tilt position issue 2018-11-04 21:40:41 +02:00
Yegor Vialov
790446d592 Resolves #161 Colors for more then 10 states in history 2018-11-04 21:36:15 +02:00
Yegor Vialov
bb17885b4a Resolves #163 Location title 2018-11-04 21:02:12 +02:00
Yegor Vialov
04d8681656 log improve and v.0.3.6 2018-11-04 19:30:10 +02:00
Yegor Vialov
71c4ac7fed v.0.3.5 2018-11-04 18:28:02 +02:00
Yegor Vialov
3f7e21e97e Fix Lovelace views int id issue 2018-11-04 18:26:31 +02:00
Yegor Vialov
e24c47b041 Error handling improvements 2018-11-04 18:20:06 +02:00
Yegor Vialov
73b32b30a8 Build number inc 2018-11-04 12:01:41 +02:00
Yegor Vialov
5b6155057c Fix views count issue on app loading 2018-11-04 11:23:21 +02:00
Yegor Vialov
ff4185effe internal build version 2018-11-03 23:33:12 +02:00
Yegor Vialov
b2da9fc04d Fix target temp history 2018-11-03 23:10:25 +02:00
Yegor Vialov
f281fab744 Version 0.3.4 2018-11-03 22:54:36 +02:00
Yegor Vialov
3b99f4feeb Resolves #120 2018-11-03 22:50:21 +02:00
Yegor Vialov
efab8b60b1 WIP #120 null values handling 2018-11-03 21:56:06 +02:00
Yegor Vialov
0e96406573 WIP #120 show all states for climate 2018-11-03 19:54:26 +02:00
Yegor Vialov
ed8757c08d Version code 2018-10-31 01:39:53 +02:00
Yegor Vialov
813770329c WIP #120 render only needed states 2018-10-31 01:37:36 +02:00
Yegor Vialov
1853bd466e WIP #120 combined history state 2018-10-31 01:02:53 +02:00
Yegor Vialov
07258477b3 Unsupported cards improvements 2018-10-30 22:53:49 +02:00
Yegor Vialov
a3adb72cf8 Unsupported lovelace cards showing entities 2018-10-30 22:51:45 +02:00
Yegor Vialov
e25162f7b5 Version code 2018-10-29 23:54:52 +02:00
Yegor Vialov
d30c9d574b WIP #120 Remove custom renderers for dots 2018-10-29 23:30:11 +02:00
Yegor Vialov
efa5a1958c WIP #120 Simple state chart improvements 2018-10-29 23:06:36 +02:00
Yegor Vialov
37f20fae5a Version code change 2018-10-29 00:59:44 +02:00
Yegor Vialov
91db34badb WIP #120 History chart based on attributes 2018-10-29 00:58:52 +02:00
Yegor Vialov
c20200b609 WIP #120 Random color for states 2018-10-28 21:02:38 +02:00
Yegor Vialov
fcd4ac7292 WIP #120 Numeric state charts 2018-10-28 20:01:01 +02:00
Yegor Vialov
e16338c3f2 WIP #120 History widget improvements 2018-10-28 18:07:52 +02:00
Yegor Vialov
6e038b0685 WIP #120 Convert history time to local 2018-10-28 15:25:12 +02:00
Yegor Vialov
052cd3894e WIP #120 Simplest on/off state history chart 2018-10-28 14:56:23 +02:00
Yegor Vialov
809c7d6355 Card separation by type 2018-10-27 17:28:47 +03:00
Yegor Vialov
9edfec7dff Code structure 2018-10-27 14:27:41 +03:00
Yegor Vialov
df56f6ceda version code change 2018-10-27 01:27:12 +03:00
Yegor Vialov
5e834b0645 Logger improvements 2018-10-27 01:24:23 +03:00
Yegor Vialov
8fb0d61a84 Resolves #122 2018-10-27 00:54:05 +03:00
Yegor Vialov
54979b583b version change for internal testing 2018-10-25 00:58:03 +03:00
Yegor Vialov
4e955e98d8 Still #154 default view 2018-10-25 00:54:20 +03:00
Yegor Vialov
88cfcb4382 Resolves #153 hidden entities 2018-10-25 00:13:50 +03:00
Yegor Vialov
5338e45ddc Resolves #154 UI building refactoring 2018-10-25 00:08:26 +03:00
Yegor Vialov
24d071e2f8 WIP #154 UI building refactoring 2018-10-24 23:53:10 +03:00
Yegor Vialov
988cd4a72f Version 0.3.3 2018-10-21 19:19:55 +03:00
Yegor Vialov
d1ea916781 Fix assumed state switch 2018-10-21 19:18:33 +03:00
Yegor Vialov
ce9f25b86c Light color button 2018-10-21 19:12:37 +03:00
Yegor Vialov
f29762c931 Fix hidden group issue 2018-10-21 18:52:29 +03:00
Yegor Vialov
30e4496ef1 Resolves #148 assumed_state support 2018-10-21 17:13:11 +03:00
Yegor Vialov
7f9dc5dd3a Set Light britness to 0 if light is turned off 2018-10-21 16:18:27 +03:00
Yegor Vialov
0f6babc243 Resolves #151 Group visibility support 2018-10-21 16:11:47 +03:00
Yegor Vialov
6a43e04b31 Just small method rename 2018-10-21 15:26:14 +03:00
Yegor Vialov
36fa5a50c4 Remove cancelling null subscription 2018-10-21 14:48:25 +03:00
Yegor Vialov
9ad6d92ccd View entities in entityCollection. Child entities in parse 2018-10-21 14:43:52 +03:00
Yegor Vialov
fafa8f43f4 Minor light fixes 2018-10-21 13:55:18 +03:00
Yegor Vialov
9b490d33d5 Reverting views refactoring 2018-10-21 02:39:51 +03:00
Yegor Vialov
33f9a1075e Remove ViewWrapper widget 2018-10-21 01:09:07 +03:00
Yegor Vialov
b83006e2c3 View as widget refactoring 2018-10-21 00:30:58 +03:00
Yegor Vialov
ba09c36bd2 Resloves #133 Light support 2018-10-18 23:47:55 +03:00
Yegor Vialov
c71ee568b0 Merge pull request #152 from estevez-dev/release/0.3.2
Fix empty cards on default_view
2018-10-18 22:03:51 +03:00
Yegor Vialov
75041f5c23 Fix empty cards on default_view 2018-10-18 21:57:10 +03:00
Yegor Vialov
14da471774 Merge pull request #150 from estevez-dev/release/0.3.1
Resolves #136 cover state
2018-10-17 21:34:36 +03:00
Yegor Vialov
369b44f1c8 Merge branch 'master' into release/0.3.1 2018-10-17 21:34:27 +03:00
Yegor Vialov
8284bb6e76 Resolves #136 cover state 2018-10-17 21:21:00 +03:00
Yegor Vialov
9b3b4dfbbc WIP #133 Lights 2018-10-17 02:19:46 +03:00
Yegor Vialov
5ca4424933 Fix dropdown width 2018-10-16 23:30:17 +03:00
Yegor Vialov
a308aa29a4 Add mode switch stateless widget 2018-10-16 23:20:27 +03:00
Yegor Vialov
9e80b0eaaf Add temperature control stateless widget 2018-10-16 22:35:17 +03:00
Yegor Vialov
85379cf491 Resolves #132 2018-10-16 21:10:59 +03:00
Yegor Vialov
758376a891 Version 0.3.0 2018-10-16 17:53:50 +03:00
Yegor Vialov
2ebba364e3 Resolves #76 Covers support 2018-10-16 17:35:13 +03:00
Yegor Vialov
6e604440c0 Resolves #106 Climate support 2018-10-16 15:14:54 +03:00
Yegor Vialov
c23034688e WIP #106 2018-10-15 18:04:16 +03:00
Yegor Vialov
69f45b52cf WIP #106 2018-10-15 00:29:40 +03:00
Yegor Vialov
ffc053fbe6 Full ui structure refactoring. InheritedWidget as entity model 2018-10-15 00:15:09 +03:00
Yegor Vialov
b5f9ecf601 Minor fixes 2018-10-12 18:03:27 +03:00
Yegor Vialov
948d1d4e23 Resolves #106 Climate support 2018-10-11 23:02:05 +03:00
Yegor Vialov
136297c18b Climate default icon. Icon colors fix 2018-10-08 23:30:09 +03:00
Yegor Vialov
164800951d Resolves #129 2018-10-08 23:11:56 +03:00
Yegor Vialov
84d283de2b VIP #120 2018-10-07 23:06:06 +03:00
Yegor Vialov
2fa35d771a Resolves #123 Account details and settings. Get user name from HA 2018-10-07 20:18:14 +03:00
Yegor Vialov
326cd073b9 Async data fetching 2018-10-07 18:27:10 +03:00
Yegor Vialov
e99c3f5742 Fix wrong password issue and infinity reconnects issue 2018-10-07 18:21:55 +03:00
Yegor Vialov
16a9392fa6 Resolves #79 Too many tabs issue 2018-10-07 17:16:24 +03:00
Yegor Vialov
5bf063969b Resolves #128 Enpty settings change issue 2018-10-07 17:07:06 +03:00
Yegor Vialov
c19a0511a6 Version 0.2.5 2018-10-07 15:08:50 +03:00
Yegor Vialov
a4ac40b366 Resolves #107 Show entity attributes 2018-10-07 15:03:51 +03:00
Yegor Vialov
ce69f044fb Resolves #110: Slider improvements 2018-10-07 12:40:45 +03:00
Yegor Vialov
70b6469bd1 Resolves #118 Fix message queue issue 2018-10-07 12:14:48 +03:00
Yegor Vialov
253316fb1f TODOs 2018-10-07 10:41:41 +03:00
Yegor Vialov
ec71200ab0 Resolves #127 Fix entities order in card 2018-10-07 10:36:50 +03:00
Yegor Vialov
bc1f4eab2e Showing error snakbar improvements. Error icon in header 2018-10-07 10:28:28 +03:00
Yegor Vialov
4085006446 Fix save settings issue 2018-10-07 09:55:37 +03:00
Yegor Vialov
b7fb821abe View now a stateful widget to prevent memory leeks 2018-10-07 09:45:04 +03:00
Yegor Vialov
284e7ba451 Resolves #125 UI building refactored 2018-10-07 02:17:14 +03:00
Yegor Vialov
17a3bd8d35 Resolves #126 Connection settings save button 2018-10-06 20:03:20 +03:00
Yegor Vialov
c2b88c8a12 Resolves #124: Connection handling improvements 2018-10-06 16:01:38 +03:00
estevez
c975af4c79 Unnecessary dependency removed 2018-10-03 21:50:11 +03:00
estevez
debf1b71f1 Remove some debug messages 2018-10-03 21:42:28 +03:00
estevez
4725953b32 Add entity widget type. Preparing to make entity build it's own badge 2018-10-03 16:44:11 +03:00
estevez
e7ca1209e2 Update app icon 2018-10-03 16:15:09 +03:00
estevez
f9afa663f5 Version code change 2018-10-03 15:55:48 +03:00
estevez
5068cbbcf4 Menu quick fix 2018-10-03 15:55:11 +03:00
estevez
043d3a9905 Changing only version code 2018-10-03 15:26:46 +03:00
estevez
77c5f80c13 Fix fetch timeout on app start 2018-10-03 15:25:01 +03:00
estevez
e0d35d07dc Version 0.2.4 2018-10-03 14:37:54 +03:00
estevez
285447a5b7 Resolves #114 Error going back from settings 2018-10-03 14:36:23 +03:00
estevez
ed3e4ba272 COnnection closing improvements 2018-10-03 10:35:40 +03:00
estevez
908563063a Fix input_boolean control 2018-10-03 09:50:14 +03:00
estevez
7f2611b410 Version 0.2.3 2018-10-03 00:55:50 +03:00
estevez
648750655c Resolves #109 No static width for inputs 2018-10-02 23:21:50 +03:00
estevez
8a0d5581d9 Resolves #111: Assumed state 2018-10-02 23:10:40 +03:00
estevez
98d716109b Resolves #21: Handling socket disconnect by sink done Future 2018-10-02 22:48:47 +03:00
estevez
ebb2f2b4e5 Decline all timeouts as variables 2018-10-02 18:05:50 +03:00
estevez
d910e4dd43 Add socket ping interval 2018-10-02 17:42:06 +03:00
estevez
95d80fbbfc Resolves #58: Message queue 2018-10-02 17:23:19 +03:00
estevez
41297150c2 Implement fetch timer with 30 timeout along with connection timer 2018-10-02 16:00:55 +03:00
estevez
b14b248f2f Resolves #72 reconnect on message sending 2018-10-02 15:46:24 +03:00
estevez
13fc1bff27 Resolves #61: Prevent second connection opening 2018-10-02 14:50:42 +03:00
estevez
eee8f21e76 Version 0.2.2 2018-10-02 00:48:25 +03:00
Yegor Vialov
8ce3560d8d Merge pull request #108 from estevez-dev/feature/entity_widget
Refactoring: Stateful entity widgets
2018-10-01 21:44:31 +00:00
estevez
9e97bac85b Refactoring: Stateful entity widgets 2018-10-02 00:41:40 +03:00
estevez
4a0b447f00 Separate entity classes on different files 2018-10-01 21:57:54 +03:00
Yegor Vialov
bc4969dae8 Resolves #104: wrong value is set for input_text 2018-10-01 10:24:38 +03:00
Yegor Vialov
5025b3d384 Merge pull request #101 from estevez-dev/release/0.2.1
Release/0.2.1
2018-09-30 20:29:12 +00:00
214 changed files with 19340 additions and 5348 deletions

22
.github/ISSUE_TEMPLATE/bug_report.md vendored Normal file
View File

@@ -0,0 +1,22 @@
---
name: Bug report
about: Create a report to help improve HA Client
title: ''
labels: ''
assignees: ''
---
**HA Client version:** [Main menu -> About HA Client]
**Home Assistant version:**
**Device name:**
**Android version:**
**Description**
[Replace with description]
**Screenshots**
[Replace with screenshots]

View File

@@ -0,0 +1,12 @@
---
name: Entity support request
about: Suggest to add support of any entity type
title: ''
labels: ENTITY, feature/improvement
assignees: ''
---
**Entity type:**
**Link to documentation:**

View File

@@ -0,0 +1,17 @@
---
name: Feature request
about: Suggest an idea for HA Client if it is not a card or entity support
title: ''
labels: feature/improvement
assignees: ''
---
**Is your feature request related to a problem? Please describe.**
A clear and concise description of what the problem is. Ex. I'm always frustrated when [...]
**Describe the solution you'd like**
A clear and concise description of what you want to happen.
**Additional context**
Add any other context or screenshots about the feature request here.

View File

@@ -0,0 +1,12 @@
---
name: Lovelace Card support request
about: Suggest to add any Lovelace card support
title: ''
labels: CARD, feature/improvement
assignees: ''
---
**Card name:**
**Link to card repository or web page:**

11
.github/no-response.yml vendored Normal file
View File

@@ -0,0 +1,11 @@
# Configuration for probot-no-response - https://github.com/probot/no-response
# Number of days of inactivity before an Issue is closed for lack of response
daysUntilClose: 14
# Label requiring a response
responseRequiredLabel: more info needed
# Comment to post when closing an Issue for lack of response. Set to `false` to disable
closeComment: >
This issue has been automatically closed because there has been no response
to our request for more information from the original author. If the issue still relevant
feel free to reopen this issue and add more information, or report a new one.

12
.gitignore vendored
View File

@@ -9,5 +9,15 @@ build/
.flutter-plugins
.idea/
.vscode/
.theia/
.project/
.settings/
key.properties
flutter_export_environment.sh
.flutter-plugins-dependencies
key.properties
.secrets.dart
pubspec.lock
google-services.json

8
.gitpod.dockerfile Normal file
View File

@@ -0,0 +1,8 @@
FROM gitpod/workspace-full:latest
ENV ANDROID_HOME=/workspace/android-sdk \
FLUTTER_ROOT=/workspace/flutter \
FLUTTER_HOME=/workspace/flutter
RUN bash -c ". /home/gitpod/.sdkman/bin/sdkman-init.sh \
&& sdk install java 8.0.242.j9-adpt"

26
.gitpod.yml Normal file
View File

@@ -0,0 +1,26 @@
image:
file: .gitpod.dockerfile
tasks:
- before: |
export PATH=$FLUTTER_HOME/bin:$FLUTTER_HOME/bin/cache/dart-sdk/bin:$ANDROID_HOME/bin:$ANDROID_HOME/platform-tools:$PATH
mkdir -p /home/gitpod/.android
touch /home/gitpod/.android/repositories.cfg
init: |
echo "Installing Flutter SDK..."
cd /workspace && wget -qO flutter_sdk.tar.xz https://storage.googleapis.com/flutter_infra/releases/stable/linux/flutter_linux_v1.12.13+hotfix.7-stable.tar.xz && tar -xf flutter_sdk.tar.xz && rm -f flutter_sdk.tar.xz
echo "Installing Android SDK..."
mkdir -p /workspace/android-sdk && cd /workspace/android-sdk && wget https://dl.google.com/android/repository/sdk-tools-linux-4333796.zip && unzip sdk-tools-linux-4333796.zip && rm -f sdk-tools-linux-4333796.zip
/workspace/android-sdk/tools/bin/sdkmanager "platform-tools" "platforms;android-28" "build-tools;28.0.3"
echo "Init Flutter..."
cd /workspace/ha_client
flutter upgrade
flutter doctor --android-licenses
flutter pub get
command: |
echo "Ready to go!"
flutter doctor
vscode:
extensions:
- Dart-Code.dart-code@3.5.0-beta.1:Wg2nTABftVR/Dry4tqeY1w==
- Dart-Code.flutter@3.5.0:/kOacEWdiDRLyN/idUiM4A==

Binary file not shown.

Binary file not shown.

View File

76
CODE_OF_CONDUCT.md Normal file
View File

@@ -0,0 +1,76 @@
# Contributor Covenant Code of Conduct
## Our Pledge
In the interest of fostering an open and welcoming environment, we as
contributors and maintainers pledge to making participation in our project and
our community a harassment-free experience for everyone, regardless of age, body
size, disability, ethnicity, sex characteristics, gender identity and expression,
level of experience, education, socio-economic status, nationality, personal
appearance, race, religion, or sexual identity and orientation.
## Our Standards
Examples of behavior that contributes to creating a positive environment
include:
* Using welcoming and inclusive language
* Being respectful of differing viewpoints and experiences
* Gracefully accepting constructive criticism
* Focusing on what is best for the community
* Showing empathy towards other community members
Examples of unacceptable behavior by participants include:
* The use of sexualized language or imagery and unwelcome sexual attention or
advances
* Trolling, insulting/derogatory comments, and personal or political attacks
* Public or private harassment
* Publishing others' private information, such as a physical or electronic
address, without explicit permission
* Other conduct which could reasonably be considered inappropriate in a
professional setting
## Our Responsibilities
Project maintainers are responsible for clarifying the standards of acceptable
behavior and are expected to take appropriate and fair corrective action in
response to any instances of unacceptable behavior.
Project maintainers have the right and responsibility to remove, edit, or
reject comments, commits, code, wiki edits, issues, and other contributions
that are not aligned to this Code of Conduct, or to ban temporarily or
permanently any contributor for other behaviors that they deem inappropriate,
threatening, offensive, or harmful.
## Scope
This Code of Conduct applies both within project spaces and in public spaces
when an individual is representing the project or its community. Examples of
representing a project or community include using an official project e-mail
address, posting via an official social media account, or acting as an appointed
representative at an online or offline event. Representation of a project may be
further defined and clarified by project maintainers.
## Enforcement
Instances of abusive, harassing, or otherwise unacceptable behavior may be
reported by contacting the project team at vyalov.egor@gmail.com. All
complaints will be reviewed and investigated and will result in a response that
is deemed necessary and appropriate to the circumstances. The project team is
obligated to maintain confidentiality with regard to the reporter of an incident.
Further details of specific enforcement policies may be posted separately.
Project maintainers who do not follow or enforce the Code of Conduct in good
faith may face temporary or permanent repercussions as determined by other
members of the project's leadership.
## Attribution
This Code of Conduct is adapted from the [Contributor Covenant][homepage], version 1.4,
available at https://www.contributor-covenant.org/version/1/4/code-of-conduct.html
[homepage]: https://www.contributor-covenant.org
For answers to common questions about this code of conduct, see
https://www.contributor-covenant.org/faq

201
LICENSE Normal file
View File

@@ -0,0 +1,201 @@
Apache License
Version 2.0, January 2004
http://www.apache.org/licenses/
TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
1. Definitions.
"License" shall mean the terms and conditions for use, reproduction,
and distribution as defined by Sections 1 through 9 of this document.
"Licensor" shall mean the copyright owner or entity authorized by
the copyright owner that is granting the License.
"Legal Entity" shall mean the union of the acting entity and all
other entities that control, are controlled by, or are under common
control with that entity. For the purposes of this definition,
"control" means (i) the power, direct or indirect, to cause the
direction or management of such entity, whether by contract or
otherwise, or (ii) ownership of fifty percent (50%) or more of the
outstanding shares, or (iii) beneficial ownership of such entity.
"You" (or "Your") shall mean an individual or Legal Entity
exercising permissions granted by this License.
"Source" form shall mean the preferred form for making modifications,
including but not limited to software source code, documentation
source, and configuration files.
"Object" form shall mean any form resulting from mechanical
transformation or translation of a Source form, including but
not limited to compiled object code, generated documentation,
and conversions to other media types.
"Work" shall mean the work of authorship, whether in Source or
Object form, made available under the License, as indicated by a
copyright notice that is included in or attached to the work
(an example is provided in the Appendix below).
"Derivative Works" shall mean any work, whether in Source or Object
form, that is based on (or derived from) the Work and for which the
editorial revisions, annotations, elaborations, or other modifications
represent, as a whole, an original work of authorship. For the purposes
of this License, Derivative Works shall not include works that remain
separable from, or merely link (or bind by name) to the interfaces of,
the Work and Derivative Works thereof.
"Contribution" shall mean any work of authorship, including
the original version of the Work and any modifications or additions
to that Work or Derivative Works thereof, that is intentionally
submitted to Licensor for inclusion in the Work by the copyright owner
or by an individual or Legal Entity authorized to submit on behalf of
the copyright owner. For the purposes of this definition, "submitted"
means any form of electronic, verbal, or written communication sent
to the Licensor or its representatives, including but not limited to
communication on electronic mailing lists, source code control systems,
and issue tracking systems that are managed by, or on behalf of, the
Licensor for the purpose of discussing and improving the Work, but
excluding communication that is conspicuously marked or otherwise
designated in writing by the copyright owner as "Not a Contribution."
"Contributor" shall mean Licensor and any individual or Legal Entity
on behalf of whom a Contribution has been received by Licensor and
subsequently incorporated within the Work.
2. Grant of Copyright License. Subject to the terms and conditions of
this License, each Contributor hereby grants to You a perpetual,
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
copyright license to reproduce, prepare Derivative Works of,
publicly display, publicly perform, sublicense, and distribute the
Work and such Derivative Works in Source or Object form.
3. Grant of Patent License. Subject to the terms and conditions of
this License, each Contributor hereby grants to You a perpetual,
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
(except as stated in this section) patent license to make, have made,
use, offer to sell, sell, import, and otherwise transfer the Work,
where such license applies only to those patent claims licensable
by such Contributor that are necessarily infringed by their
Contribution(s) alone or by combination of their Contribution(s)
with the Work to which such Contribution(s) was submitted. If You
institute patent litigation against any entity (including a
cross-claim or counterclaim in a lawsuit) alleging that the Work
or a Contribution incorporated within the Work constitutes direct
or contributory patent infringement, then any patent licenses
granted to You under this License for that Work shall terminate
as of the date such litigation is filed.
4. Redistribution. You may reproduce and distribute copies of the
Work or Derivative Works thereof in any medium, with or without
modifications, and in Source or Object form, provided that You
meet the following conditions:
(a) You must give any other recipients of the Work or
Derivative Works a copy of this License; and
(b) You must cause any modified files to carry prominent notices
stating that You changed the files; and
(c) You must retain, in the Source form of any Derivative Works
that You distribute, all copyright, patent, trademark, and
attribution notices from the Source form of the Work,
excluding those notices that do not pertain to any part of
the Derivative Works; and
(d) If the Work includes a "NOTICE" text file as part of its
distribution, then any Derivative Works that You distribute must
include a readable copy of the attribution notices contained
within such NOTICE file, excluding those notices that do not
pertain to any part of the Derivative Works, in at least one
of the following places: within a NOTICE text file distributed
as part of the Derivative Works; within the Source form or
documentation, if provided along with the Derivative Works; or,
within a display generated by the Derivative Works, if and
wherever such third-party notices normally appear. The contents
of the NOTICE file are for informational purposes only and
do not modify the License. You may add Your own attribution
notices within Derivative Works that You distribute, alongside
or as an addendum to the NOTICE text from the Work, provided
that such additional attribution notices cannot be construed
as modifying the License.
You may add Your own copyright statement to Your modifications and
may provide additional or different license terms and conditions
for use, reproduction, or distribution of Your modifications, or
for any such Derivative Works as a whole, provided Your use,
reproduction, and distribution of the Work otherwise complies with
the conditions stated in this License.
5. Submission of Contributions. Unless You explicitly state otherwise,
any Contribution intentionally submitted for inclusion in the Work
by You to the Licensor shall be under the terms and conditions of
this License, without any additional terms or conditions.
Notwithstanding the above, nothing herein shall supersede or modify
the terms of any separate license agreement you may have executed
with Licensor regarding such Contributions.
6. Trademarks. This License does not grant permission to use the trade
names, trademarks, service marks, or product names of the Licensor,
except as required for reasonable and customary use in describing the
origin of the Work and reproducing the content of the NOTICE file.
7. Disclaimer of Warranty. Unless required by applicable law or
agreed to in writing, Licensor provides the Work (and each
Contributor provides its Contributions) on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
implied, including, without limitation, any warranties or conditions
of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
PARTICULAR PURPOSE. You are solely responsible for determining the
appropriateness of using or redistributing the Work and assume any
risks associated with Your exercise of permissions under this License.
8. Limitation of Liability. In no event and under no legal theory,
whether in tort (including negligence), contract, or otherwise,
unless required by applicable law (such as deliberate and grossly
negligent acts) or agreed to in writing, shall any Contributor be
liable to You for damages, including any direct, indirect, special,
incidental, or consequential damages of any character arising as a
result of this License or out of the use or inability to use the
Work (including but not limited to damages for loss of goodwill,
work stoppage, computer failure or malfunction, or any and all
other commercial damages or losses), even if such Contributor
has been advised of the possibility of such damages.
9. Accepting Warranty or Additional Liability. While redistributing
the Work or Derivative Works thereof, You may choose to offer,
and charge a fee for, acceptance of support, warranty, indemnity,
or other liability obligations and/or rights consistent with this
License. However, in accepting such obligations, You may act only
on Your own behalf and on Your sole responsibility, not on behalf
of any other Contributor, and only if You agree to indemnify,
defend, and hold each Contributor harmless for any liability
incurred by, or claims asserted against, such Contributor by reason
of your accepting any such warranty or additional liability.
END OF TERMS AND CONDITIONS
APPENDIX: How to apply the Apache License to your work.
To apply the Apache License to your work, attach the following
boilerplate notice, with the fields enclosed by brackets "[]"
replaced with your own identifying information. (Don't include
the brackets!) The text should be enclosed in the appropriate
comment syntax for the file format. We also recommend that a
file or class name and description of purpose be included on the
same "printed page" as the copyright notice for easier
identification within third-party archives.
Copyright [yyyy] [name of copyright owner]
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.

View File

@@ -1,3 +1,18 @@
# Android client for Home Assistant
# HA Client
## Native Android client for Home Assistant
### With actionable notifications, location tracking and Lovelace UI support
Home Assistant Android client using Flutter and Dart.
Visit [ha-client.app](http://ha-client.app/) for more info.
Download the app from [Google Play](https://play.google.com/store/apps/details?id=com.keyboardcrumbs.haclient)
Discuss it on [Discord](https://discord.gg/u9vq7QE) or at [Home Assistant community](https://community.home-assistant.io/c/mobile-apps/ha-client-android)
[![Gitpod Ready-to-Code](https://img.shields.io/badge/Gitpod-Ready--to--Code-blue?logo=gitpod)](https://gitpod.io/#https://github.com/estevez-dev/ha_client)
#### Last release build status
[![Codemagic build status](https://api.codemagic.io/apps/5da8bdab9f20ef798f7c2c65/5db1862025dc3f0b0288a57a/status_badge.svg)](https://codemagic.io/apps/5da8bdab9f20ef798f7c2c65/5db1862025dc3f0b0288a57a/latest_build)
#### Special thanks to
- [Crewski](https://github.com/Crewski) for his [HANotify](https://github.com/Crewski/HANotify)
- [Home Assistant](https://github.com/home-assistant) for some support and [Home Assistant](https://www.home-assistant.io/)

1
android/.gitignore vendored
View File

@@ -8,3 +8,4 @@
/build
/captures
GeneratedPluginRegistrant.java
.project/

17
android/.project Normal file
View File

@@ -0,0 +1,17 @@
<?xml version="1.0" encoding="UTF-8"?>
<projectDescription>
<name>android</name>
<comment>Project android created by Buildship.</comment>
<projects>
</projects>
<buildSpec>
<buildCommand>
<name>org.eclipse.buildship.core.gradleprojectbuilder</name>
<arguments>
</arguments>
</buildCommand>
</buildSpec>
<natures>
<nature>org.eclipse.buildship.core.gradleprojectnature</nature>
</natures>
</projectDescription>

6
android/app/.classpath Normal file
View File

@@ -0,0 +1,6 @@
<?xml version="1.0" encoding="UTF-8"?>
<classpath>
<classpathentry kind="con" path="org.eclipse.jdt.launching.JRE_CONTAINER/org.eclipse.jdt.internal.debug.ui.launcher.StandardVMType/JavaSE-1.8/"/>
<classpathentry kind="con" path="org.eclipse.buildship.core.gradleclasspathcontainer"/>
<classpathentry kind="output" path="bin/default"/>
</classpath>

23
android/app/.project Normal file
View File

@@ -0,0 +1,23 @@
<?xml version="1.0" encoding="UTF-8"?>
<projectDescription>
<name>app</name>
<comment>Project app created by Buildship.</comment>
<projects>
</projects>
<buildSpec>
<buildCommand>
<name>org.eclipse.jdt.core.javabuilder</name>
<arguments>
</arguments>
</buildCommand>
<buildCommand>
<name>org.eclipse.buildship.core.gradleprojectbuilder</name>
<arguments>
</arguments>
</buildCommand>
</buildSpec>
<natures>
<nature>org.eclipse.jdt.core.javanature</nature>
<nature>org.eclipse.buildship.core.gradleprojectnature</nature>
</natures>
</projectDescription>

View File

@@ -29,7 +29,12 @@ def keystoreProperties = new Properties()
keystoreProperties.load(new FileInputStream(keystorePropertiesFile))
android {
compileSdkVersion 27
compileSdkVersion 28
compileOptions {
sourceCompatibility JavaVersion.VERSION_1_8
targetCompatibility JavaVersion.VERSION_1_8
}
lintOptions {
disable 'InvalidPackage'
@@ -38,13 +43,21 @@ android {
defaultConfig {
applicationId "com.keyboardcrumbs.haclient"
minSdkVersion 21
targetSdkVersion 27
targetSdkVersion 28
versionCode flutterVersionCode.toInteger()
versionName flutterVersionName
testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner"
}
signingConfigs {
if (!System.getenv()["CI"]) {
debug {
keyAlias keystoreProperties['debugKeyAlias']
keyPassword keystoreProperties['debugKeyPassword']
storeFile file(keystoreProperties['debugStoreFile'])
storePassword keystoreProperties['debugStorePassword']
}
}
release {
keyAlias keystoreProperties['keyAlias']
keyPassword keystoreProperties['keyPassword']
@@ -65,7 +78,13 @@ flutter {
}
dependencies {
implementation 'com.google.firebase:firebase-analytics:17.2.2'
implementation 'com.google.firebase:firebase-messaging:20.2.0'
implementation 'androidx.work:work-runtime:2.3.4'
testImplementation 'junit:junit:4.12'
androidTestImplementation 'com.android.support.test:runner:1.0.2'
androidTestImplementation 'com.android.support.test.espresso:espresso-core:3.0.2'
}
apply plugin: 'io.fabric'
apply plugin: 'com.google.gms.google-services'

View File

@@ -1,11 +1,14 @@
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.keyboardcrumbs.hassclient">
<!-- The INTERNET permission is required for development. Specifically,
flutter needs it to communicate with the running application
to allow setting breakpoints, to provide hot reload, etc.
-->
<uses-feature android:name="android.hardware.touchscreen"
android:required="false" />
<uses-permission android:name="android.permission.INTERNET"/>
<uses-permission android:name="android.permission.VIBRATE" />
<uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" />
<uses-permission android:name="android.permission.RECEIVE_BOOT_COMPLETED"/>
<uses-permission android:name="android.permission.WAKE_LOCK"/>
<!-- io.flutter.app.FlutterApplication is an android.app.Application that
calls FlutterMain.startInitialization(this); in its onCreate method.
@@ -13,9 +16,19 @@
additional functionality it is fine to subclass or reimplement
FlutterApplication and put your custom class here. -->
<application
android:name="io.flutter.app.FlutterApplication"
android:label="HA Client"
android:icon="@mipmap/ic_launcher">
android:icon="@mipmap/ic_launcher"
android:roundIcon="@mipmap/ic_launcher_round"
android:usesCleartextTraffic="true">
<meta-data
android:name="flutterEmbedding"
android:value="2" />
<meta-data
android:name="com.google.firebase.messaging.default_notification_channel_id"
android:value="ha_notify" />
<activity
android:name=".MainActivity"
android:launchMode="singleTop"
@@ -23,17 +36,45 @@
android:configChanges="orientation|keyboardHidden|keyboard|screenSize|locale|layoutDirection|fontScale|screenLayout|density"
android:hardwareAccelerated="true"
android:windowSoftInputMode="adjustResize">
<!-- This keeps the window background of the activity showing
until Flutter renders its first frame. It can be removed if
there is no splash screen (such as the default splash screen
defined in @style/LaunchTheme). -->
<meta-data
android:name="io.flutter.app.android.SplashScreenUntilFirstFrame"
android:value="true" />
android:name="io.flutter.embedding.android.SplashScreenDrawable"
android:resource="@drawable/launch_background" />
<meta-data
android:name="io.flutter.embedding.android.NormalTheme"
android:resource="@style/NormalTheme" />
<intent-filter>
<action android:name="android.intent.action.MAIN"/>
<category android:name="android.intent.category.LAUNCHER"/>
</intent-filter>
</activity>
<service
android:name=".MessagingService"
android:exported="false">
<intent-filter>
<action android:name="com.google.firebase.MESSAGING_EVENT" />
</intent-filter>
</service>
<receiver android:name=".NotificationActionReceiver" android:exported="true">
<intent-filter>
<action android:name="android.intent.action.BOOT_COMPLETED"/>
<action android:name="android.intent.action.INPUT_METHOD_CHANGED" />
</intent-filter>
</receiver>
<service
android:name="io.flutter.plugins.androidalarmmanager.AlarmService"
android:permission="android.permission.BIND_JOB_SERVICE"
android:exported="false"/>
<receiver
android:name="io.flutter.plugins.androidalarmmanager.AlarmBroadcastReceiver"
android:exported="false"/>
<receiver
android:name="io.flutter.plugins.androidalarmmanager.RebootBroadcastReceiver"
android:enabled="false">
<intent-filter>
<action android:name="android.intent.action.BOOT_COMPLETED"></action>
</intent-filter>
</receiver>
</application>
</manifest>
</manifest>

View File

@@ -1,13 +1,71 @@
package com.keyboardcrumbs.hassclient;
import android.os.Bundle;
import io.flutter.app.FlutterActivity;
import androidx.annotation.NonNull;
import io.flutter.embedding.android.FlutterActivity;
import io.flutter.embedding.engine.FlutterEngine;
import io.flutter.plugins.GeneratedPluginRegistrant;
import android.content.Context;
import android.content.SharedPreferences;
import android.content.SharedPreferences.Editor;
import android.os.Bundle;
import io.flutter.plugin.common.MethodCall;
import io.flutter.plugin.common.MethodChannel;
import com.google.android.gms.tasks.OnCompleteListener;
import com.google.android.gms.tasks.Task;
import com.google.android.gms.common.GoogleApiAvailability;
import com.google.android.gms.common.ConnectionResult;
import com.google.firebase.iid.FirebaseInstanceId;
import com.google.firebase.iid.InstanceIdResult;
import com.google.firebase.messaging.FirebaseMessaging;
public class MainActivity extends FlutterActivity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
GeneratedPluginRegistrant.registerWith(this);
}
private static final String CHANNEL = "com.keyboardcrumbs.hassclient/native";
@Override
public void configureFlutterEngine(@NonNull FlutterEngine flutterEngine) {
GeneratedPluginRegistrant.registerWith(flutterEngine);
new MethodChannel(flutterEngine.getDartExecutor().getBinaryMessenger(), CHANNEL).setMethodCallHandler(
new MethodChannel.MethodCallHandler() {
@Override
public void onMethodCall(MethodCall call, MethodChannel.Result result) {
Context context = getActivity();
if (call.method.equals("getFCMToken")) {
if (checkPlayServices()) {
FirebaseInstanceId.getInstance().getInstanceId()
.addOnCompleteListener(new OnCompleteListener<InstanceIdResult>() {
@Override
public void onComplete(@NonNull Task<InstanceIdResult> task) {
if (task.isSuccessful()) {
String token = task.getResult().getToken();
UpdateTokenTask updateTokenTask = new UpdateTokenTask(context);
updateTokenTask.execute(token);
result.success(token);
} else {
result.error("fcm_error", task.getException().getMessage(), task.getException());
}
}
});
} else {
result.error("google_play_service_error", "Google Play Services unavailable", null);
}
}
}
}
);
}
private boolean checkPlayServices() {
return (GoogleApiAvailability.getInstance().isGooglePlayServicesAvailable(this) == ConnectionResult.SUCCESS);
}
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
}
}

View File

@@ -0,0 +1,152 @@
package com.keyboardcrumbs.hassclient;
import java.util.Map;
import java.net.URL;
import java.net.URLConnection;
import java.io.IOException;
import java.io.InputStream;
import android.app.NotificationChannel;
import android.app.NotificationManager;
import android.app.PendingIntent;
import android.content.Context;
import android.content.Intent;
import android.media.RingtoneManager;
import android.net.Uri;
import android.os.Build;
import androidx.core.app.NotificationCompat;
import com.google.firebase.messaging.FirebaseMessagingService;
import com.google.firebase.messaging.RemoteMessage;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.webkit.URLUtil;
public class MessagingService extends FirebaseMessagingService {
private static final String TAG = "MessagingService";
@Override
public void onMessageReceived(RemoteMessage remoteMessage) {
Map<String, String> data = remoteMessage.getData();
if (data.size() > 0) {
if (data.containsKey("body") || data.containsKey("title")) {
sendNotification(data);
}
}
}
@Override
public void onNewToken(String token) {
UpdateTokenTask updateTokenTask = new UpdateTokenTask(this);
updateTokenTask.execute(token);
}
private void sendNotification(Map<String, String> data) {
String channelId, messageBody, messageTitle, imageUrl, nTag, channelDescription;
boolean autoCancel;
if (!data.containsKey("channelId")) {
channelId = "ha_notify";
channelDescription = "Default notification channel";
} else {
channelId = data.get("channelId");
channelDescription = channelId;
}
if (!data.containsKey("body")) {
messageBody = "";
} else {
messageBody = data.get("body");
}
if (!data.containsKey("title")) {
messageTitle = "HA Client";
} else {
messageTitle = data.get("title");
}
if (!data.containsKey("tag")) {
nTag = String.valueOf(System.currentTimeMillis());
} else {
nTag = data.get("tag");
}
if (data.containsKey("dismiss")) {
try {
boolean dismiss = Boolean.parseBoolean(data.get("dismiss"));
if (dismiss) {
NotificationManager notificationManager =
(NotificationManager) getSystemService(Context.NOTIFICATION_SERVICE);
notificationManager.cancel(nTag, 0);
return;
}
} catch (Exception e) {
//nope
}
}
if (data.containsKey("autoDismiss")) {
try {
autoCancel = Boolean.parseBoolean(data.get("autoDismiss"));
} catch (Exception e) {
autoCancel = true;
}
} else {
autoCancel = true;
}
imageUrl = data.get("image");
Intent intent = new Intent(this, MainActivity.class);
intent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP);
PendingIntent pendingIntent = PendingIntent.getActivity(this, 0 /* Request code */, intent,
PendingIntent.FLAG_ONE_SHOT);
Uri defaultSoundUri = RingtoneManager.getDefaultUri(RingtoneManager.TYPE_NOTIFICATION);
NotificationCompat.Builder notificationBuilder =
new NotificationCompat.Builder(this, channelId)
.setSmallIcon(R.drawable.mini_icon)
.setContentTitle(messageTitle)
.setContentText(messageBody)
.setAutoCancel(autoCancel)
.setSound(defaultSoundUri)
.setContentIntent(pendingIntent);
if (URLUtil.isValidUrl(imageUrl)) {
Bitmap image = getBitmapFromURL(imageUrl);
if (image != null) {
notificationBuilder.setStyle(new NotificationCompat.BigPictureStyle().bigPicture(image).bigLargeIcon(BitmapFactory.decodeResource(getResources(), R.drawable.blank_icon)));
notificationBuilder.setLargeIcon(image);
}
}
for (int i = 1; i <= 3; i++) {
if (data.containsKey("action" + i)) {
Intent broadcastIntent = new Intent(this, NotificationActionReceiver.class);
if (autoCancel) {
broadcastIntent.putExtra("tag", nTag);
}
broadcastIntent.putExtra("actionData", data.get("action" + i + "_data"));
PendingIntent actionIntent = PendingIntent.getBroadcast(this, i, broadcastIntent, PendingIntent.FLAG_CANCEL_CURRENT);
notificationBuilder.addAction(R.drawable.mini_icon, data.get("action" + i), actionIntent);
}
}
NotificationManager notificationManager =
(NotificationManager) getSystemService(Context.NOTIFICATION_SERVICE);
// Since android Oreo notification channel is needed.
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
NotificationChannel channel = new NotificationChannel(channelId,
channelDescription,
NotificationManager.IMPORTANCE_HIGH);
notificationManager.createNotificationChannel(channel);
}
notificationManager.notify(nTag, 0 /* ID of notification */, notificationBuilder.build());
}
private Bitmap getBitmapFromURL(String imageUrl) {
try {
URL url = new URL(imageUrl);
URLConnection connection = url.openConnection();
connection.setDoInput(true);
connection.connect();
InputStream input = connection.getInputStream();
return BitmapFactory.decodeStream(input);
} catch (IOException e) {
return null;
}
}
}

View File

@@ -0,0 +1,69 @@
package com.keyboardcrumbs.hassclient;
import android.content.Context;
import androidx.annotation.NonNull;
import android.util.Log;
import android.content.BroadcastReceiver;
import android.content.Intent;
import android.app.NotificationManager;
import android.webkit.URLUtil;
import org.json.JSONObject;
import android.content.SharedPreferences;
public class NotificationActionReceiver extends BroadcastReceiver {
private static final String TAG = "NotificationActionReceiver";
@Override
public void onReceive(Context context, Intent intent) {
String rawActionData = intent.getStringExtra("actionData");
if (intent.hasExtra("tag")) {
String notificationTag = intent.getStringExtra("tag");
NotificationManager notificationManager = (NotificationManager)context.getSystemService(Context.NOTIFICATION_SERVICE);
notificationManager.cancel(notificationTag, 0);
}
SharedPreferences prefs = context.getSharedPreferences("FlutterSharedPreferences", Context.MODE_PRIVATE);
String webhookId = prefs.getString("flutter.app-webhook-id", null);
if (webhookId != null) {
try {
String requestUrl = prefs.getString("flutter.hassio-res-protocol", "") +
"://" +
prefs.getString("flutter.hassio-domain", "") +
":" +
prefs.getString("flutter.hassio-port", "") + "/api/webhook/" + webhookId;
JSONObject actionData = new JSONObject(rawActionData);
if (URLUtil.isValidUrl(requestUrl)) {
JSONObject dataToSend = new JSONObject();
JSONObject requestData = new JSONObject();
if (actionData.getString("action").equals("call-service")) {
dataToSend.put("type", "call_service");
requestData.put("domain", actionData.getString("service").split("\\.")[0]);
requestData.put("service", actionData.getString("service").split("\\.")[1]);
if (actionData.has("service_data")) {
requestData.put("service_data", actionData.get("service_data"));
}
} else {
dataToSend.put("type", "fire_event");
requestData.put("event_type", "ha_client_event");
JSONObject eventData = new JSONObject();
eventData.put("action", actionData.getString("action"));
requestData.put("event_data", eventData);
}
dataToSend.put("data", requestData);
String stringRequest = dataToSend.toString();
SendTask sendTask = new SendTask();
sendTask.execute(requestUrl, stringRequest);
} else {
Log.w(TAG, "Invalid HA url");
}
} catch (Exception e) {
Log.e(TAG, "Error handling notification action", e);
}
} else {
Log.w(TAG, "Webhook id not found");
}
}
}

View File

@@ -0,0 +1,46 @@
package com.keyboardcrumbs.hassclient;
import android.util.Log;
import android.os.AsyncTask;
import java.net.URL;
import java.net.HttpURLConnection;
import java.io.OutputStream;
public class SendTask extends AsyncTask<String, String, String> {
private static final String TAG = "SendTask";
public SendTask(){
//set context variables if required
}
@Override
protected void onPreExecute() {
super.onPreExecute();
}
@Override
protected String doInBackground(String... params) {
String urlString = params[0];
String data = params[1];
try {
URL url = new URL(urlString);
HttpURLConnection urlConnection = (HttpURLConnection) url.openConnection();
urlConnection.setRequestMethod("POST");
urlConnection.setRequestProperty("Content-Type", "application/json");
urlConnection.setDoOutput(true);
byte[] outputBytes = data.getBytes("UTF-8");
OutputStream os = urlConnection.getOutputStream();
os.write(outputBytes);
int responseCode = urlConnection.getResponseCode();
urlConnection.disconnect();
} catch (Exception e) {
Log.e(TAG, "Error sending data", e);
}
return null;
}
}

View File

@@ -0,0 +1,46 @@
package com.keyboardcrumbs.hassclient;
import android.util.Log;
import android.os.AsyncTask;
import java.net.URL;
import java.net.HttpURLConnection;
import java.io.OutputStream;
import android.webkit.URLUtil;
import org.json.JSONObject;
import android.content.SharedPreferences;
import android.content.Context;
import java.lang.ref.WeakReference;
public class UpdateTokenTask extends AsyncTask<String, String, String> {
private static final String TAG = "UpdateTokenTask";
private WeakReference<Context> contextRef;
public UpdateTokenTask(Context context){
contextRef = new WeakReference<>(context);
}
@Override
protected void onPreExecute() {
super.onPreExecute();
}
@Override
protected String doInBackground(String... params) {
Log.d(TAG, "Updating push token");
Context context = contextRef.get();
if (context != null) {
String token = params[0];
SharedPreferences prefs = context.getSharedPreferences("FlutterSharedPreferences", Context.MODE_PRIVATE);
SharedPreferences.Editor editor = prefs.edit();
editor.putString("flutter.npush-token", token);
editor.commit();
}
return null;
}
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 461 B

View File

@@ -0,0 +1,27 @@
<?xml version="1.0" encoding="utf-8"?>
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="108dp"
android:height="108dp"
android:viewportWidth="108"
android:viewportHeight="108">
<group>
<clip-path
android:pathData="M 0 0 H 108 V 108 H 0 V 0 Z" />
<path
android:fillColor="#709ac1"
android:fillAlpha="0"
android:pathData="M 0 0 H 108 V 108 H 0 V 0 Z" />
<path
android:fillColor="#000000"
android:fillAlpha="0.12"
android:pathData="M 70.506 38.389 L 108 72.466 L 108 108 L 77 108 L 35.066 72.466 L 38.373 63.769 L 36.268 50.216 L 43.335 44.523 L 51.841 34.578 L 63.096 42.478 L 68.586 42.478 L 70.506 38.389 Z" />
<path
android:fillColor="#000000"
android:fillAlpha="0.12"
android:pathData="M 28.979 53.708 L 47.736 67.31 L 38.373 58.563 L 36.268 51.52 L 28.979 53.708 Z" />
<path
android:fillColor="#ffffff"
android:pathData="M 77.131 54.24 L 72.878 54.24 L 72.878 72.415 L 56.339 72.415 L 56.339 64.85 L 62.931 58.511 L 64.609 58.784 C 67.349 58.784 69.57 56.649 69.57 54.013 C 69.57 51.378 67.349 49.242 64.609 49.242 C 61.868 49.242 59.647 51.378 59.647 54.013 L 59.883 55.626 L 56.339 59.079 L 56.339 46.63 C 57.898 45.812 58.938 44.244 58.938 42.427 C 58.938 39.792 56.717 37.656 53.976 37.656 C 51.236 37.656 49.015 39.792 49.015 42.427 C 49.015 44.244 50.054 45.812 51.614 46.63 L 51.614 59.079 L 48.07 55.626 L 48.306 54.013 C 48.306 51.378 46.084 49.242 43.344 49.242 C 40.604 49.242 38.383 51.378 38.383 54.013 C 38.383 56.648 40.604 58.784 43.344 58.784 L 45.022 58.511 L 51.614 64.85 L 51.614 72.415 L 35.075 72.415 L 35.075 54.24 L 30.94 54.24 C 29.948 54.24 28.979 54.24 28.979 53.763 C 29.003 53.263 29.995 52.309 31.011 51.332 L 51.614 31.522 C 52.393 30.772 53.197 30 53.976 30 C 54.756 30 55.559 30.772 56.339 31.522 L 65.79 40.609 L 65.79 38.338 L 70.515 38.338 L 70.515 45.153 L 77.084 51.469 C 78.029 52.377 78.997 53.309 79.021 53.786 C 79.021 54.24 78.076 54.24 77.131 54.24 Z M 43.344 51.969 C 43.908 51.969 44.449 52.184 44.848 52.567 C 45.247 52.951 45.471 53.471 45.471 54.013 C 45.471 54.555 45.247 55.076 44.848 55.459 C 44.449 55.842 43.908 56.058 43.344 56.058 C 42.78 56.058 42.239 55.842 41.841 55.459 C 41.442 55.076 41.218 54.555 41.218 54.013 C 41.218 53.471 41.442 52.951 41.841 52.567 C 42.239 52.184 42.78 51.969 43.344 51.969 Z M 64.609 51.969 C 65.79 51.969 66.735 52.877 66.735 54.013 C 66.735 55.149 65.79 56.058 64.609 56.058 C 64.045 56.058 63.504 55.842 63.105 55.459 C 62.706 55.076 62.482 54.555 62.482 54.013 C 62.482 53.471 62.706 52.951 63.105 52.567 C 63.504 52.184 64.045 51.969 64.609 51.969 Z M 53.976 40.382 C 55.158 40.382 56.103 41.291 56.103 42.427 C 56.103 43.563 55.158 44.472 53.976 44.472 C 52.795 44.472 51.85 43.563 51.85 42.427 C 51.85 41.291 52.795 40.382 53.976 40.382 Z" />
</group>
</vector>

View File

@@ -1,7 +1,7 @@
<?xml version="1.0" encoding="utf-8"?>
<!-- Modify this file to customize your launch splash screen -->
<layer-list xmlns:android="http://schemas.android.com/apk/res/android">
<item android:drawable="@android:color/white" />
<item android:drawable="@color/main_color" />
<!-- You can insert your own image assets here -->
<!-- <item>

Binary file not shown.

After

Width:  |  Height:  |  Size: 612 B

View File

@@ -0,0 +1,5 @@
<?xml version="1.0" encoding="utf-8"?>
<adaptive-icon xmlns:android="http://schemas.android.com/apk/res/android">
<background android:drawable="@color/main_color"/>
<foreground android:drawable="@drawable/ic_launcher_foreground" />
</adaptive-icon>

View File

@@ -0,0 +1,5 @@
<?xml version="1.0" encoding="utf-8"?>
<adaptive-icon xmlns:android="http://schemas.android.com/apk/res/android">
<background android:drawable="@color/main_color"/>
<foreground android:drawable="@drawable/ic_launcher_foreground" />
</adaptive-icon>

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.2 KiB

After

Width:  |  Height:  |  Size: 3.8 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 6.1 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.5 KiB

After

Width:  |  Height:  |  Size: 2.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.3 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 3.0 KiB

After

Width:  |  Height:  |  Size: 5.0 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 8.0 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 4.5 KiB

After

Width:  |  Height:  |  Size: 9.8 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 15 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 5.6 KiB

After

Width:  |  Height:  |  Size: 14 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 20 KiB

View File

@@ -1,8 +1,12 @@
<?xml version="1.0" encoding="utf-8"?>
<resources>
<color name="main_color">#709AC1</color>
<style name="LaunchTheme" parent="@android:style/Theme.Black.NoTitleBar">
<!-- Show a splash screen on the activity. Automatically removed when
Flutter draws its first frame -->
<item name="android:windowBackground">@drawable/launch_background</item>
</style>
<style name="NormalTheme" parent="@android:style/Theme.Black.NoTitleBar">
<item name="android:windowBackground">@drawable/launch_background</item>
</style>
</resources>

View File

@@ -2,10 +2,15 @@ buildscript {
repositories {
google()
jcenter()
maven {
url 'https://maven.fabric.io/public'
}
}
dependencies {
classpath 'com.android.tools.build:gradle:3.1.2'
classpath 'com.android.tools.build:gradle:3.3.2'
classpath 'com.google.gms:google-services:4.3.3'
classpath 'io.fabric.tools:gradle:1.26.1'
}
}
@@ -13,6 +18,9 @@ allprojects {
repositories {
google()
jcenter()
maven {
url 'https://maven.fabric.io/public'
}
}
}

View File

@@ -1 +1,6 @@
org.gradle.jvmargs=-Xmx1536M
org.gradle.jvmargs=-Xmx2g
org.gradle.daemon=true
org.gradle.caching=true
android.useAndroidX=true
android.enableJetifier=true
android.enableR8=true

View File

@@ -3,4 +3,4 @@ distributionBase=GRADLE_USER_HOME
distributionPath=wrapper/dists
zipStoreBase=GRADLE_USER_HOME
zipStorePath=wrapper/dists
distributionUrl=https\://services.gradle.org/distributions/gradle-4.4-all.zip
distributionUrl=https\://services.gradle.org/distributions/gradle-4.10.1-all.zip

0
android/gradlew vendored Normal file → Executable file
View File

View File

@@ -0,0 +1 @@
include ':app'

View File

@@ -0,0 +1,61 @@
<html>
<head>
<script src="https://cdn.jsdelivr.net/npm/hls.js@latest"></script>
<style>
body {
padding: 0;
margin: 0;
widows: 100%;
height: 100%;
}
video {
width: 100%;
}
</style>
<script>
var messageChannel = '{{message_channel}}';
</script>
</head>
<body>
<video id="screen" width="100%" controls></video>
<script>
if (Hls.isSupported()) {
var video = document.getElementById('screen');
var hls = new Hls();
hls.on(Hls.Events.ERROR, function (event, data) {
if (data.fatal) {
switch(data.type) {
case Hls.ErrorTypes.NETWORK_ERROR:
// try to recover network error
console.log("fatal network error encountered, try to recover");
hls.startLoad();
break;
case Hls.ErrorTypes.MEDIA_ERROR:
console.log("fatal media error encountered, try to recover");
hls.recoverMediaError();
break;
default:
// cannot recover
hls.destroy();
break;
}
}
});
// bind them together
hls.attachMedia(video);
hls.on(Hls.Events.MEDIA_ATTACHED, function () {
console.log("video and hls.js are now bound together !");
hls.loadSource("{{stream_url}}");
hls.on(Hls.Events.MANIFEST_PARSED, function (event, data) {
console.log("manifest loaded, found " + data.levels.length + " quality level");
video.play();
video.onloadedmetadata = function() {
window[messageChannel].postMessage(document.body.clientWidth / video.offsetHeight);
};
});
});
}
</script>
</body>
</html>

View File

@@ -0,0 +1,28 @@
<html>
<head>
<style>
body {
padding: 0;
margin: 0;
widows: 100%;
height: 100%;
}
img {
width: 100%;
}
</style>
<script>
var messageChannel = '{{message_channel}}';
window.onload = function() {
var img = document.getElementById('screen');
if (img) {
window[messageChannel].postMessage(document.body.clientWidth / img.offsetHeight);
}
};
</script>
</head>
<body>
<img id="screen" src="{{stream_url}}">
</body>
</html>

37
assets/js/externalAuth.js Normal file
View File

@@ -0,0 +1,37 @@
window.externalApp = {};
window.externalApp.getExternalAuth = function(options) {
console.log("Starting external auth");
var options = JSON.parse(options);
if (options && options.callback) {
var responseData = {
access_token: "[token]",
expires_in: 1800
};
console.log("Waiting for callback to be added");
setTimeout(function(){
console.log("Calling a callback");
window[options.callback](true, responseData);
}, 900);
}
};
/*
window.externalApp.externalBus = function(message) {
console.log("External bus message: " + message);
var messageObj = JSON.parse(message);
if (messageObj.type == "config/get") {
var responseData = {
id: messageObj.id,
type: "result",
success: true,
result: {
hasSettingsScreen: true
}
};
setTimeout(function(){
window.externalBus(responseData);
}, 500);
} else if (messageObj.type == "config_screen/show") {
HAClient.postMessage('show-settings');
}
};
*/

1
docs/empty Normal file
View File

@@ -0,0 +1 @@

BIN
docs/ha_access_tokens.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 23 KiB

BIN
docs/ha_profile-300x247.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 16 KiB

BIN
docs/settings-869x1024.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 102 KiB

41
flutter_01.log Normal file
View File

@@ -0,0 +1,41 @@
Flutter crash report; please file at https://github.com/flutter/flutter/issues.
## command
flutter --no-color run --machine --track-widget-creation --device-id=89AY052S4 lib/main.dart
## exception
_InternalLinkedHashMap<String, dynamic>: {code: 105, message: Isolate must be runnable, data: {request: {method: _reloadSources, params: {pause: true, rootLibUri: file:///data/user/0/com.keyboardcrumbs.haclient/code_cache/ha_clientSYJJZI/ha_client/lib/main.dart.incremental.dill, packagesUri: file:///data/user/0/com.keyboardcrumbs.haclient/code_cache/ha_clientSYJJZI/ha_client/.packages, isolateId: isolates/68989666}}, details: Isolate must be runnable before this request is made.}}
```
null```
## flutter doctor
```
[✓] Flutter (Channel stable, v1.7.8+hotfix.4, on Linux, locale en_US.UTF-8)
• Flutter version 1.7.8+hotfix.4 at /home/estevez/sdk/flutter
• Framework revision 20e59316b8 (6 weeks ago), 2019-07-18 20:04:33 -0700
• Engine revision fee001c93f
• Dart version 2.4.0
[✓] Android toolchain - develop for Android devices (Android SDK version 29.0.2)
• Android SDK at /home/estevez/Android/Sdk
• Android NDK location not configured (optional; useful for native profiling support)
• Platform android-29, build-tools 29.0.2
• Java binary at: /home/estevez/bin/android-studio/jre/bin/java
• Java version OpenJDK Runtime Environment (build 1.8.0_202-release-1483-b49-5587405)
• All Android licenses accepted.
[✓] Android Studio (version 3.5)
• Android Studio at /home/estevez/bin/android-studio
• Flutter plugin version 38.2.3
• Dart plugin version 191.8423
• Java version OpenJDK Runtime Environment (build 1.8.0_202-release-1483-b49-5587405)
[✓] Connected device (1 available)
• Pixel 3 XL • 89AY052S4 • android-arm64 • Android 9 (API 28)
• No issues found!
```

41
flutter_02.log Normal file
View File

@@ -0,0 +1,41 @@
Flutter crash report; please file at https://github.com/flutter/flutter/issues.
## command
flutter --no-color run --machine --track-widget-creation --device-id=89AY052S4 lib/main.dart
## exception
_InternalLinkedHashMap<String, dynamic>: {code: 105, message: Isolate must be runnable, data: {request: {method: _reloadSources, params: {pause: true, rootLibUri: file:///data/user/0/com.keyboardcrumbs.haclient/code_cache/ha_clientWYMXDL/ha_client/lib/main.dart.incremental.dill, packagesUri: file:///data/user/0/com.keyboardcrumbs.haclient/code_cache/ha_clientWYMXDL/ha_client/.packages, isolateId: isolates/289688365}}, details: Isolate must be runnable before this request is made.}}
```
null```
## flutter doctor
```
[✓] Flutter (Channel stable, v1.7.8+hotfix.4, on Linux, locale en_US.UTF-8)
• Flutter version 1.7.8+hotfix.4 at /home/estevez/sdk/flutter
• Framework revision 20e59316b8 (6 weeks ago), 2019-07-18 20:04:33 -0700
• Engine revision fee001c93f
• Dart version 2.4.0
[✓] Android toolchain - develop for Android devices (Android SDK version 29.0.2)
• Android SDK at /home/estevez/Android/Sdk
• Android NDK location not configured (optional; useful for native profiling support)
• Platform android-29, build-tools 29.0.2
• Java binary at: /home/estevez/bin/android-studio/jre/bin/java
• Java version OpenJDK Runtime Environment (build 1.8.0_202-release-1483-b49-5587405)
• All Android licenses accepted.
[✓] Android Studio (version 3.5)
• Android Studio at /home/estevez/bin/android-studio
• Flutter plugin version 38.2.3
• Dart plugin version 191.8423
• Java version OpenJDK Runtime Environment (build 1.8.0_202-release-1483-b49-5587405)
[✓] Connected device (1 available)
• Pixel 3 XL • 89AY052S4 • android-arm64 • Android 9 (API 28)
• No issues found!
```

41
flutter_03.log Normal file
View File

@@ -0,0 +1,41 @@
Flutter crash report; please file at https://github.com/flutter/flutter/issues.
## command
flutter --no-color run --machine --track-widget-creation --device-id=89AY052S4 lib/main.dart
## exception
_InternalLinkedHashMap<String, dynamic>: {code: 105, message: Isolate must be runnable, data: {request: {method: _reloadSources, params: {pause: true, rootLibUri: file:///data/user/0/com.keyboardcrumbs.haclient/code_cache/ha_clientLNSJAH/ha_client/lib/main.dart.incremental.dill, packagesUri: file:///data/user/0/com.keyboardcrumbs.haclient/code_cache/ha_clientLNSJAH/ha_client/.packages, isolateId: isolates/866521062}}, details: Isolate must be runnable before this request is made.}}
```
null```
## flutter doctor
```
[✓] Flutter (Channel stable, v1.7.8+hotfix.4, on Linux, locale en_US.UTF-8)
• Flutter version 1.7.8+hotfix.4 at /home/estevez/sdk/flutter
• Framework revision 20e59316b8 (6 weeks ago), 2019-07-18 20:04:33 -0700
• Engine revision fee001c93f
• Dart version 2.4.0
[✓] Android toolchain - develop for Android devices (Android SDK version 29.0.2)
• Android SDK at /home/estevez/Android/Sdk
• Android NDK location not configured (optional; useful for native profiling support)
• Platform android-29, build-tools 29.0.2
• Java binary at: /home/estevez/bin/android-studio/jre/bin/java
• Java version OpenJDK Runtime Environment (build 1.8.0_202-release-1483-b49-5587405)
• All Android licenses accepted.
[✓] Android Studio (version 3.5)
• Android Studio at /home/estevez/bin/android-studio
• Flutter plugin version 38.2.3
• Dart plugin version 191.8423
• Java version OpenJDK Runtime Environment (build 1.8.0_202-release-1483-b49-5587405)
[✓] Connected device (1 available)
• Pixel 3 XL • 89AY052S4 • android-arm64 • Android 9 (API 28)
• No issues found!
```

Binary file not shown.

Binary file not shown.

Before

Width:  |  Height:  |  Size: 13 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 12 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 43 KiB

After

Width:  |  Height:  |  Size: 48 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 711 B

After

Width:  |  Height:  |  Size: 715 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.3 KiB

After

Width:  |  Height:  |  Size: 1.4 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.2 KiB

After

Width:  |  Height:  |  Size: 2.0 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.1 KiB

After

Width:  |  Height:  |  Size: 1.0 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.1 KiB

After

Width:  |  Height:  |  Size: 2.0 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 3.0 KiB

After

Width:  |  Height:  |  Size: 2.9 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.3 KiB

After

Width:  |  Height:  |  Size: 1.4 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.6 KiB

After

Width:  |  Height:  |  Size: 2.7 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 3.8 KiB

After

Width:  |  Height:  |  Size: 3.9 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 3.8 KiB

After

Width:  |  Height:  |  Size: 3.9 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 5.9 KiB

After

Width:  |  Height:  |  Size: 5.7 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.7 KiB

After

Width:  |  Height:  |  Size: 2.5 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 4.7 KiB

After

Width:  |  Height:  |  Size: 4.9 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 5.5 KiB

After

Width:  |  Height:  |  Size: 5.3 KiB

View File

@@ -1,9 +0,0 @@
part of 'main.dart';
class Badge {
String _entityId;
Badge(String groupId) {
_entityId = groupId;
}
}

View File

@@ -1,25 +0,0 @@
part of 'main.dart';
class HACard {
String _entityId;
List _entities;
String _friendlyName;
List get entities => _entities;
String get friendlyName => _friendlyName;
HACard(String groupId, String friendlyName) {
_entityId = groupId;
_entities = [];
_friendlyName = friendlyName;
}
void addEntity(String entityId) {
_entities.add(entityId);
}
void addEntities(List entities) {
_entities.addAll(entities);
}
}

View File

@@ -0,0 +1,62 @@
part of '../main.dart';
class AlarmPanelCard extends StatelessWidget {
final AlarmPanelCardData card;
const AlarmPanelCard({Key key, this.card}) : super(key: key);
@override
Widget build(BuildContext context) {
if (card.entity.entity.statelessType == StatelessEntityType.missed) {
return EntityModel(
entityWrapper: card.entity,
child: MissedEntityWidget(),
handleTap: false,
);
}
List<Widget> body = [];
body.add(CardHeader(
name: card.name ?? "",
subtitle: Text("${card.entity.entity.displayState}",
),
trailing: Row(
mainAxisSize: MainAxisSize.min,
mainAxisAlignment: MainAxisAlignment.end,
children: [
EntityIcon(
size: 50.0,
),
Container(
width: 26.0,
child: IconButton(
padding: EdgeInsets.all(0.0),
alignment: Alignment.centerRight,
icon: Icon(MaterialDesignIcons.getIconDataFromIconName(
"mdi:dots-vertical")),
onPressed: () => eventBus.fire(new ShowEntityPageEvent(entityId: card.entity.entity.entityId))
)
)
]
),
));
body.add(
AlarmControlPanelControlsWidget(
extended: true,
states: card.states,
)
);
return CardWrapper(
child: EntityModel(
entityWrapper: card.entity,
handleTap: null,
child: Column(
crossAxisAlignment: CrossAxisAlignment.center,
mainAxisSize: MainAxisSize.min,
children: body
)
)
);
}
}

196
lib/cards/badges.dart Normal file
View File

@@ -0,0 +1,196 @@
part of '../main.dart';
class Badges extends StatelessWidget {
final BadgesData badges;
const Badges({Key key, this.badges}) : super(key: key);
@override
Widget build(BuildContext context) {
List<EntityWrapper> entitiesToShow = badges.getEntitiesToShow();
if (entitiesToShow.isNotEmpty) {
if (AppSettings().scrollBadges) {
return ConstrainedBox(
constraints: BoxConstraints.tightFor(height: 112),
child: SingleChildScrollView(
scrollDirection: Axis.horizontal,
child: Row(
mainAxisSize: MainAxisSize.min,
mainAxisAlignment: MainAxisAlignment.center,
crossAxisAlignment: CrossAxisAlignment.start,
children: entitiesToShow.map((entity) =>
EntityModel(
entityWrapper: entity,
child: Padding(
padding: EdgeInsets.fromLTRB(5, 10, 5, 10),
child: BadgeWidget(),
),
handleTap: true,
)).toList()
),
)
);
} else {
return Padding(
padding: EdgeInsets.fromLTRB(5, 10, 5, 10),
child: Wrap(
alignment: WrapAlignment.center,
spacing: 10.0,
runSpacing: 5,
children: entitiesToShow.map((entity) =>
EntityModel(
entityWrapper: entity,
child: BadgeWidget(),
handleTap: true,
)).toList(),
)
);
}
}
return Container(height: 0.0, width: 0.0,);
}
}
class BadgeWidget extends StatelessWidget {
@override
Widget build(BuildContext context) {
final entityModel = EntityModel.of(context);
Widget badgeIcon;
String onBadgeTextValue;
Color iconColor = HAClientTheme().getBadgeColor(entityModel.entityWrapper.entity.domain);
switch (entityModel.entityWrapper.entity.domain) {
case "sun":
{
IconData iconData;
if (entityModel.entityWrapper.entity.state == "below_horizon") {
iconData = MaterialDesignIcons.getIconDataFromIconCode(0xf0dc);
} else {
iconData = MaterialDesignIcons.getIconDataFromIconCode(0xf5a8);
}
badgeIcon = Padding(
padding: EdgeInsets.all(10),
child: Icon(
iconData,
)
);
break;
}
case "camera":
case "media_player":
case "binary_sensor":
{
badgeIcon = EntityIcon(
imagePadding: EdgeInsets.all(0.0),
iconPadding: EdgeInsets.all(10),
color: Theme.of(context).textTheme.body2.color
);
break;
}
case "device_tracker":
case "person":
{
badgeIcon = EntityIcon(
imagePadding: EdgeInsets.all(0.0),
iconPadding: EdgeInsets.all(10),
color: Theme.of(context).textTheme.body2.color
);
onBadgeTextValue = entityModel.entityWrapper.entity.displayState;
break;
}
default:
{
onBadgeTextValue = entityModel.entityWrapper.unitOfMeasurement;
badgeIcon = Padding(
padding: EdgeInsets.all(4),
child: Text(
"${entityModel.entityWrapper.entity.displayState}",
overflow: TextOverflow.fade,
softWrap: false,
textAlign: TextAlign.center,
style: Theme.of(context).textTheme.body1
)
);
break;
}
}
Widget onBadgeText;
if (onBadgeTextValue == null || onBadgeTextValue.length == 0) {
onBadgeText = Container(width: 0.0, height: 0.0);
} else {
onBadgeText = Container(
constraints: BoxConstraints(maxWidth: 50),
padding: EdgeInsets.fromLTRB(6.0, 2.0, 6.0, 2.0),
child: Text("$onBadgeTextValue",
style: Theme.of(context).textTheme.overline.copyWith(
color: HAClientTheme().getOnBadgeTextColor()
),
textAlign: TextAlign.center,
softWrap: false,
overflow: TextOverflow.ellipsis
),
decoration: new BoxDecoration(
color: iconColor,
borderRadius: BorderRadius.circular(9.0),
)
);
}
return GestureDetector(
child: Column(
mainAxisSize: MainAxisSize.min,
children: <Widget>[
Stack(
overflow: Overflow.visible,
alignment: Alignment.center,
children: <Widget>[
Container(
width: 45,
height: 45,
decoration: new BoxDecoration(
// Circle shape
shape: BoxShape.circle,
color: Theme.of(context).cardColor,
// The border you want
border: Border.all(
width: 2.0,
color: iconColor,
),
),
),
SizedBox(
width: 41,
height: 41,
child: FittedBox(
fit: BoxFit.contain,
alignment: Alignment.center,
child: badgeIcon,
)
),
Positioned(
bottom: -6,
child: onBadgeText
)
],
),
Container(
constraints: BoxConstraints(maxWidth: 45),
padding: EdgeInsets.only(top: 10),
child: Text(
"${entityModel.entityWrapper.displayName}",
textAlign: TextAlign.center,
style: Theme.of(context).textTheme.caption.copyWith(
fontSize: 10
),
softWrap: true,
maxLines: 3,
overflow: TextOverflow.ellipsis,
),
)
],
),
onTap: () => entityModel.entityWrapper.handleTap(),
onDoubleTap: () => entityModel.entityWrapper.handleDoubleTap(),
onLongPress: () => entityModel.entityWrapper.handleHold(),
);
}
}

666
lib/cards/card.class.dart Normal file
View File

@@ -0,0 +1,666 @@
part of '../main.dart';
class CardData {
String type;
List<EntityWrapper> entities = [];
List conditions;
bool showEmpty;
List stateFilter;
bool stateColor = true;
EntityWrapper get entity => entities.isNotEmpty ? entities[0] : null;
factory CardData.parse(rawData) {
try {
if (rawData['type'] == null) {
rawData['type'] = CardType.ENTITIES;
} else if (!(rawData['type'] is String)) {
return CardData(null);
}
switch (rawData['type']) {
case CardType.ENTITIES:
case CardType.HISTORY_GRAPH:
case CardType.MAP:
case CardType.PICTURE_GLANCE:
case CardType.SENSOR:
case CardType.ENTITY:
case CardType.WEATHER_FORECAST:
case CardType.PLANT_STATUS:
if (rawData['entity'] != null) {
rawData['entities'] = [rawData['entity']];
}
return EntitiesCardData(rawData);
break;
case CardType.ALARM_PANEL:
return AlarmPanelCardData(rawData);
break;
case CardType.LIGHT:
return LightCardData(rawData);
break;
case CardType.PICTURE_ELEMENTS:
//TODO temporary solution
if (rawData.containsKey('camera_image')) {
rawData['entity'] = rawData['camera_image'];
return ButtonCardData(rawData);
} else {
return CardData(null);
}
break;
case CardType.ENTITY_BUTTON:
case CardType.BUTTON:
case CardType.PICTURE_ENTITY:
return ButtonCardData(rawData);
break;
case CardType.CONDITIONAL:
return CardData.parse(rawData['card']);
break;
case CardType.ENTITY_FILTER:
Map cardData = Map.from(rawData);
cardData.remove('type');
if (rawData.containsKey('card')) {
cardData.addAll(rawData['card']);
}
cardData['type'] ??= CardType.ENTITIES;
return CardData.parse(cardData);
break;
case CardType.GAUGE:
return GaugeCardData(rawData);
break;
case CardType.GLANCE:
case CardType.THERMOSTAT:
if (rawData['entity'] != null) {
rawData['entities'] = [rawData['entity']];
}
return GlanceCardData(rawData);
break;
case CardType.HORIZONTAL_STACK:
return HorizontalStackCardData(rawData);
break;
case CardType.VERTICAL_STACK:
return VerticalStackCardData(rawData);
break;
case CardType.MARKDOWN:
return MarkdownCardData(rawData);
break;
case CardType.MEDIA_CONTROL:
return MediaControlCardData(rawData);
break;
case CardType.BADGES:
return BadgesData(rawData);
break;
default:
return CardData(null);
}
} catch (error, stacktrace) {
Logger.e('Error parsing card $rawData: $error', stacktrace: stacktrace);
return ErrorCardData(rawData);
}
}
CardData(rawData) {
if (rawData != null && rawData is Map) {
type = rawData['type'];
conditions = rawData['conditions'] ?? [];
showEmpty = rawData['show_empty'] ?? true;
stateFilter = rawData['state_filter'] ?? [];
} else {
type = CardType.UNKNOWN;
conditions = [];
showEmpty = true;
stateFilter = [];
}
}
Widget buildCardWidget() {
return UnsupportedCard(card: this);
}
List<EntityWrapper> getEntitiesToShow() {
return entities.where((entityWrapper) {
if (entityWrapper.entity.isHidden) {
return false;
}
List currentStateFilter;
if (entityWrapper.stateFilter != null && entityWrapper.stateFilter.isNotEmpty) {
currentStateFilter = entityWrapper.stateFilter;
} else {
currentStateFilter = stateFilter;
}
bool showByFilter = currentStateFilter.isEmpty;
for (var allowedState in currentStateFilter) {
if (allowedState is String && allowedState == entityWrapper.entity.state) {
showByFilter = true;
break;
} else if (allowedState is Map) {
try {
var tmpVal = allowedState['attribute'] != null ? entityWrapper.entity.getAttribute(allowedState['attribute']) : entityWrapper.entity.state;
var valToCompareWith = allowedState['value'];
var valToCompare;
if (valToCompareWith is! String && tmpVal is String) {
valToCompare = double.tryParse(tmpVal);
} else {
valToCompare = tmpVal;
}
if (valToCompare != null) {
bool result;
switch (allowedState['operator']) {
case '<=': { result = valToCompare <= valToCompareWith;}
break;
case '<': { result = valToCompare < valToCompareWith;}
break;
case '>=': { result = valToCompare >= valToCompareWith;}
break;
case '>': { result = valToCompare > valToCompareWith;}
break;
case '!=': { result = valToCompare != valToCompareWith;}
break;
case 'regex': {
RegExp regExp = RegExp(valToCompareWith.toString());
result = regExp.hasMatch(valToCompare.toString());
}
break;
default: {
result = valToCompare == valToCompareWith;
}
}
if (result) {
showByFilter = true;
break;
}
}
} catch (e, stacktrace) {
Logger.e('Error filtering ${entityWrapper.entity.entityId} by $allowedState: $e', stacktrace: stacktrace);
}
}
}
return showByFilter;
}).toList();
}
}
class BadgesData extends CardData {
String title;
String icon;
bool showHeaderToggle;
@override
Widget buildCardWidget() {
return Badges(badges: this);
}
BadgesData(rawData) : super(rawData) {
if (rawData['badges'] is List) {
rawData['badges'].forEach((dynamic rawBadge) {
if (rawBadge is String && HomeAssistant().entities.isExist(rawBadge)) {
entities.add(EntityWrapper(entity: HomeAssistant().entities.get(rawBadge)));
} else if (rawBadge is Map && rawBadge.containsKey('entity') && HomeAssistant().entities.isExist(rawBadge['entity'])) {
entities.add(
EntityWrapper(
entity: HomeAssistant().entities.get(rawBadge['entity']),
overrideName: rawBadge["name"]?.toString(),
overrideIcon: rawBadge["icon"],
)
);
} else if (rawBadge is Map && rawBadge.containsKey('entities')) {
_parseEntities(rawBadge);
}
});
}
}
void _parseEntities(rawData) {
var rawEntities = rawData['entities'] ?? [];
rawEntities.forEach((rawEntity) {
if (rawEntity is String) {
if (HomeAssistant().entities.isExist(rawEntity)) {
entities.add(EntityWrapper(
entity: HomeAssistant().entities.get(rawEntity),
stateFilter: rawData['state_filter'] ?? [],
));
}
} else if (HomeAssistant().entities.isExist('${rawEntity['entity']}')) {
Entity e = HomeAssistant().entities.get(rawEntity["entity"]);
entities.add(
EntityWrapper(
entity: e,
overrideName: rawEntity["name"]?.toString(),
overrideIcon: rawEntity["icon"],
stateFilter: rawEntity['state_filter'] ?? (rawData['state_filter'] ?? []),
uiAction: EntityUIAction(rawEntityData: rawEntity)
)
);
}
});
}
}
class EntitiesCardData extends CardData {
String title;
String icon;
bool showHeaderToggle;
@override
Widget buildCardWidget() {
return EntitiesCard(card: this);
}
EntitiesCardData(rawData) : super(rawData) {
//Parsing card data
title = rawData['title']?.toString();
icon = rawData['icon'] is String ? rawData['icon'] : null;
stateColor = rawData['state_color'] ?? false;
showHeaderToggle = rawData['show_header_toggle'] ?? false;
//Parsing entities
var rawEntities = rawData['entities'] ?? [];
rawEntities.forEach((rawEntity) {
if (rawEntity is String) {
if (HomeAssistant().entities.isExist(rawEntity)) {
entities.add(EntityWrapper(entity: HomeAssistant().entities.get(rawEntity)));
} else {
entities.add(EntityWrapper(entity: Entity.missed(rawEntity)));
}
} else {
if (rawEntity["type"] == "divider") {
entities.add(EntityWrapper(entity: Entity.divider()));
} else if (rawEntity["type"] == "section") {
entities.add(EntityWrapper(entity: Entity.section(rawEntity["label"] ?? "")));
} else if (rawEntity["type"] == "call-service") {
Map uiActionData = {
"tap_action": {
"action": EntityUIAction.callService,
"service": rawEntity["service"],
"service_data": rawEntity["service_data"]
},
"hold_action": EntityUIAction.none
};
entities.add(
EntityWrapper(
entity: Entity.callService(
icon: rawEntity["icon"],
name: rawEntity["name"]?.toString(),
service: rawEntity["service"],
actionName: rawEntity["action_name"]
),
stateColor: rawEntity["state_color"] ?? stateColor,
uiAction: EntityUIAction(rawEntityData: uiActionData)
)
);
} else if (rawEntity["type"] == "weblink") {
Map uiActionData = {
"tap_action": {
"action": EntityUIAction.navigate,
"service": rawEntity["url"]
},
"hold_action": EntityUIAction.none
};
entities.add(EntityWrapper(
entity: Entity.weblink(
icon: rawEntity["icon"],
name: rawEntity["name"]?.toString(),
url: rawEntity["url"]
),
stateColor: rawEntity["state_color"] ?? stateColor,
uiAction: EntityUIAction(rawEntityData: uiActionData)
)
);
} else if (HomeAssistant().entities.isExist(rawEntity["entity"])) {
Entity e = HomeAssistant().entities.get(rawEntity["entity"]);
entities.add(
EntityWrapper(
entity: e,
stateColor: rawEntity["state_color"] ?? stateColor,
overrideName: rawEntity["name"]?.toString(),
overrideIcon: rawEntity["icon"],
stateFilter: rawEntity['state_filter'] ?? [],
uiAction: EntityUIAction(rawEntityData: rawEntity)
)
);
} else {
entities.add(EntityWrapper(entity: Entity.missed(rawEntity["entity"])));
}
}
});
}
}
class AlarmPanelCardData extends CardData {
String name;
List<dynamic> states;
@override
Widget buildCardWidget() {
return AlarmPanelCard(card: this);
}
AlarmPanelCardData(rawData) : super(rawData) {
//Parsing card data
name = rawData['name']?.toString();
states = rawData['states'];
//Parsing entity
var entitiId = rawData["entity"];
if (entitiId != null && entitiId is String) {
if (HomeAssistant().entities.isExist(entitiId)) {
entities.add(EntityWrapper(
entity: HomeAssistant().entities.get(entitiId),
stateColor: true,
overrideName: name
));
} else {
entities.add(EntityWrapper(entity: Entity.missed(entitiId)));
}
}
}
}
class LightCardData extends CardData {
String name;
String icon;
@override
Widget buildCardWidget() {
return LightCard(card: this);
}
LightCardData(rawData) : super(rawData) {
//Parsing card data
name = rawData['name']?.toString();
icon = rawData['icon'] is String ? rawData['icon'] : null;
//Parsing entity
var entitiId = rawData["entity"];
if (entitiId != null && entitiId is String) {
if (HomeAssistant().entities.isExist(entitiId)) {
entities.add(EntityWrapper(
entity: HomeAssistant().entities.get(entitiId),
overrideName: name,
overrideIcon: icon,
uiAction: EntityUIAction()..tapAction = EntityUIAction.toggle
));
} else {
entities.add(EntityWrapper(entity: Entity.missed(entitiId)));
}
} else {
entities.add(EntityWrapper(entity: Entity.missed('$entitiId')));
}
}
}
class ButtonCardData extends CardData {
String name;
String icon;
bool showName;
bool showIcon;
double iconHeightPx = 0;
double iconHeightRem = 0;
@override
Widget buildCardWidget() {
return EntityButtonCard(card: this);
}
ButtonCardData(rawData) : super(rawData) {
//Parsing card data
name = rawData['name']?.toString();
icon = rawData['icon'] is String ? rawData['icon'] : null;
showName = rawData['show_name'] ?? true;
showIcon = rawData['show_icon'] ?? true;
stateColor = rawData['state_color'] ?? true;
var rawHeight = rawData['icon_height'];
if (rawHeight != null && rawHeight is String) {
if (rawHeight.contains('px')) {
iconHeightPx = double.tryParse(rawHeight.replaceFirst('px', '')) ?? 0;
} else if (rawHeight.contains('rem')) {
iconHeightRem = double.tryParse(rawHeight.replaceFirst('rem', '')) ?? 0;
} else if (rawHeight.contains('em')) {
iconHeightRem = double.tryParse(rawHeight.replaceFirst('em', '')) ?? 0;
}
}
//Parsing entity
var entitiId = rawData["entity"];
if (entitiId != null && entitiId is String) {
if (HomeAssistant().entities.isExist(entitiId)) {
entities.add(EntityWrapper(
entity: HomeAssistant().entities.get(entitiId),
overrideName: name,
overrideIcon: icon,
stateColor: stateColor,
uiAction: EntityUIAction(
rawEntityData: rawData
)
));
} else {
entities.add(EntityWrapper(entity: Entity.missed(entitiId)));
}
} else if (entitiId == null) {
entities.add(
EntityWrapper(
entity: Entity.ghost(
name,
icon,
),
stateColor: stateColor,
uiAction: EntityUIAction(
rawEntityData: rawData
)
)
);
}
}
}
class GaugeCardData extends CardData {
String name;
String unit;
double min;
double max;
Map severity;
@override
Widget buildCardWidget() {
return GaugeCard(card: this);
}
GaugeCardData(rawData) : super(rawData) {
//Parsing card data
name = rawData['name']?.toString();
unit = rawData['unit'];
if (rawData['min'] is int) {
min = rawData['min'].toDouble();
} else if (rawData['min'] is double) {
min = rawData['min'];
} else {
min = 0;
}
if (rawData['max'] is int) {
max = rawData['max'].toDouble();
} else if (rawData['max'] is double) {
max = rawData['max'];
} else {
max = 100;
}
severity = rawData['severity'];
//Parsing entity
var entitiId = rawData["entity"] is List ? rawData["entity"][0] : rawData["entity"];
if (entitiId != null && entitiId is String) {
if (HomeAssistant().entities.isExist(entitiId)) {
entities.add(EntityWrapper(
entity: HomeAssistant().entities.get(entitiId),
overrideName: name
));
} else {
entities.add(EntityWrapper(entity: Entity.missed(entitiId)));
}
} else {
entities.add(EntityWrapper(entity: Entity.missed('$entitiId')));
}
}
}
class GlanceCardData extends CardData {
String title;
bool showName;
bool showIcon;
bool showState;
bool stateColor;
int columnsCount;
@override
Widget buildCardWidget() {
return GlanceCard(card: this);
}
GlanceCardData(rawData) : super(rawData) {
//Parsing card data
title = rawData["title"]?.toString();
showName = rawData['show_name'] ?? true;
showIcon = rawData['show_icon'] ?? true;
showState = rawData['show_state'] ?? true;
stateColor = rawData['state_color'] ?? true;
columnsCount = rawData['columns'] ?? 4;
//Parsing entities
var rawEntities = rawData["entities"] ?? [];
rawEntities.forEach((rawEntity) {
if (rawEntity is String) {
if (HomeAssistant().entities.isExist(rawEntity)) {
entities.add(EntityWrapper(entity: HomeAssistant().entities.get(rawEntity)));
} else {
entities.add(EntityWrapper(entity: Entity.missed(rawEntity)));
}
} else {
if (HomeAssistant().entities.isExist(rawEntity["entity"])) {
Entity e = HomeAssistant().entities.get(rawEntity["entity"]);
entities.add(
EntityWrapper(
entity: e,
stateColor: stateColor,
overrideName: rawEntity["name"]?.toString(),
overrideIcon: rawEntity["icon"],
stateFilter: rawEntity['state_filter'] ?? [],
uiAction: EntityUIAction(rawEntityData: rawEntity)
)
);
} else {
entities.add(EntityWrapper(entity: Entity.missed(rawEntity["entity"])));
}
}
});
}
}
class HorizontalStackCardData extends CardData {
List<CardData> childCards;
@override
Widget buildCardWidget() {
return HorizontalStackCard(card: this);
}
HorizontalStackCardData(rawData) : super(rawData) {
if (rawData.containsKey('cards') && rawData['cards'] is List) {
childCards = rawData['cards'].map<CardData>((childCard) {
return CardData.parse(childCard);
}).toList();
} else {
childCards = [];
}
}
}
class VerticalStackCardData extends CardData {
List<CardData> childCards;
@override
Widget buildCardWidget() {
return VerticalStackCard(card: this);
}
VerticalStackCardData(rawData) : super(rawData) {
if (rawData.containsKey('cards') && rawData['cards'] is List) {
childCards = rawData['cards'].map<CardData>((childCard) {
return CardData.parse(childCard);
}).toList();
} else {
childCards = [];
}
}
}
class MarkdownCardData extends CardData {
String title;
String content;
@override
Widget buildCardWidget() {
return MarkdownCard(card: this);
}
MarkdownCardData(rawData) : super(rawData) {
//Parsing card data
title = rawData['title'];
content = rawData['content'];
}
}
class MediaControlCardData extends CardData {
@override
Widget buildCardWidget() {
return MediaControlsCard(card: this);
}
MediaControlCardData(rawData) : super(rawData) {
var entitiId = rawData["entity"];
if (entitiId != null && entitiId is String) {
if (HomeAssistant().entities.isExist(entitiId)) {
entities.add(EntityWrapper(
entity: HomeAssistant().entities.get(entitiId),
));
} else {
entities.add(EntityWrapper(entity: Entity.missed(entitiId)));
}
}
}
}
class ErrorCardData extends CardData {
String cardConfig;
@override
Widget buildCardWidget() {
return ErrorCard(card: this);
}
ErrorCardData(rawData) : super(rawData) {
cardConfig = '$rawData';
}
}

View File

@@ -0,0 +1,79 @@
part of '../main.dart';
class EntitiesCard extends StatelessWidget {
final EntitiesCardData card;
const EntitiesCard({Key key, this.card}) : super(key: key);
@override
Widget build(BuildContext context) {
List<EntityWrapper> entitiesToShow = card.getEntitiesToShow();
if (entitiesToShow.isEmpty && !card.showEmpty) {
return Container(height: 0.0, width: 0.0,);
}
List<Widget> body = [];
Widget headerSwitch;
if (card.showHeaderToggle) {
bool headerToggleVal = entitiesToShow.any((EntityWrapper en){ return en.entity.state == EntityState.on; });
List<String> entitiesToToggle = entitiesToShow.where((EntityWrapper enw) {
return <String>["switch", "light", "automation", "input_boolean"].contains(enw.entity.domain);
}).map((EntityWrapper en) {
return en.entity.entityId;
}).toList();
headerSwitch = Switch(
value: headerToggleVal,
onChanged: (val) {
if (entitiesToToggle.isNotEmpty) {
ConnectionManager().callService(
domain: "homeassistant",
service: val ? "turn_on" : "turn_off",
entityId: entitiesToToggle
);
}
},
);
}
body.add(
CardHeader(
name: card.title,
trailing: headerSwitch,
emptyPadding: Sizes.rowPadding,
leading: card.icon != null ? Icon(
MaterialDesignIcons.getIconDataFromIconName(card.icon),
size: Sizes.iconSize,
color: Theme.of(context).textTheme.headline.color
) : null,
)
);
body.addAll(
entitiesToShow.map((EntityWrapper entity) {
return Padding(
padding: EdgeInsets.fromLTRB(0.0, 4.0, 0.0, 4.0),
child: EntityModel(
entityWrapper: entity,
handleTap: true,
child: entity.entity.buildDefaultWidget(context)
),
);
})
);
return CardWrapper(
child: Padding(
padding: EdgeInsets.only(
right: Sizes.rightWidgetPadding,
left: Sizes.leftWidgetPadding,
bottom: Sizes.rowPadding,
),
child: Center(
child: Column(
mainAxisSize: MainAxisSize.min,
mainAxisAlignment: MainAxisAlignment.start,
children: body
)
),
)
);
}
}

View File

@@ -0,0 +1,96 @@
part of '../main.dart';
class EntityButtonCard extends StatelessWidget {
final ButtonCardData card;
EntityButtonCard({
Key key, this.card
}) : super(key: key);
@override
Widget build(BuildContext context) {
EntityWrapper entityWrapper = card.entity;
if (entityWrapper.entity.statelessType == StatelessEntityType.missed) {
return EntityModel(
entityWrapper: card.entity,
child: MissedEntityWidget(),
handleTap: false,
);
} else if (entityWrapper.entity.statelessType != StatelessEntityType.ghost && entityWrapper.entity.statelessType != StatelessEntityType.none) {
return Container(width: 0.0, height: 0.0,);
}
double iconSize = math.max(card.iconHeightPx, card.iconHeightRem * Theme.of(context).textTheme.body1.fontSize);
Widget buttonIcon;
if (!card.showIcon) {
buttonIcon = Container(height: Sizes.rowPadding, width: 10);
} else if (iconSize > 0) {
buttonIcon = SizedBox(
height: iconSize,
child: FractionallySizedBox(
widthFactor: 0.5,
child: FittedBox(
fit: BoxFit.contain,
child: EntityIcon(
//padding: EdgeInsets.only(top: 6),
),
)
),
);
} else {
buttonIcon = AspectRatio(
aspectRatio: 2,
child: FractionallySizedBox(
widthFactor: 0.5,
child: FittedBox(
fit: BoxFit.fitWidth,
child: EntityIcon(
//padding: EdgeInsets.only(top: 6),
),
)
),
);
}
return CardWrapper(
child: EntityModel(
entityWrapper: card.entity,
child: InkWell(
onTap: () => entityWrapper.handleTap(),
onLongPress: () => entityWrapper.handleHold(),
onDoubleTap: () => entityWrapper.handleDoubleTap(),
child: Center(
child: Padding(
padding: EdgeInsets.only(top: 5),
child: Column(
mainAxisSize: MainAxisSize.min,
crossAxisAlignment: CrossAxisAlignment.stretch,
children: <Widget>[
buttonIcon,
_buildName(context)
],
)
)
),
),
handleTap: true
)
);
}
Widget _buildName(BuildContext context) {
if (card.showName) {
return EntityName(
padding: EdgeInsets.fromLTRB(Sizes.buttonPadding, 0.0, Sizes.buttonPadding, Sizes.rowPadding),
textOverflow: TextOverflow.ellipsis,
maxLines: 3,
textStyle: Theme.of(context).textTheme.subhead,
wordsWrap: true,
textAlign: TextAlign.center
);
}
return Container(width: 0, height: 0);
}
}

38
lib/cards/error_card.dart Normal file
View File

@@ -0,0 +1,38 @@
part of '../main.dart';
class ErrorCard extends StatelessWidget {
final ErrorCardData card;
const ErrorCard({Key key, this.card}) : super(key: key);
@override
Widget build(BuildContext context) {
return CardWrapper(
child: Padding(
padding: EdgeInsets.fromLTRB(Sizes.leftWidgetPadding, Sizes.rowPadding, Sizes.rightWidgetPadding, Sizes.rowPadding),
child: Column(
mainAxisSize: MainAxisSize.min,
crossAxisAlignment: CrossAxisAlignment.center,
children: <Widget>[
Text(
'There was an error rendering card: ${card.type}. Please copy card config to clipboard and report this issue. Thanks!',
textAlign: TextAlign.center,
),
RaisedButton(
onPressed: () {
Clipboard.setData(new ClipboardData(text: card.cardConfig));
},
child: Text('Copy card config'),
),
RaisedButton(
onPressed: () {
Launcher.launchURLInBrowser("https://github.com/estevez-dev/ha_client/issues/new?assignees=&labels=&template=bug_report.md&title=");
},
child: Text('Report issue'),
)
],
),
)
);
}
}

201
lib/cards/gauge_card.dart Normal file
View File

@@ -0,0 +1,201 @@
part of '../main.dart';
class GaugeCard extends StatelessWidget {
final GaugeCardData card;
GaugeCard({Key key, this.card}) : super(key: key);
@override
Widget build(BuildContext context) {
EntityWrapper entityWrapper = card.entity;
if (entityWrapper.entity.statelessType == StatelessEntityType.missed) {
return EntityModel(
entityWrapper: card.entity,
child: MissedEntityWidget(),
handleTap: false,
);
}
entityWrapper.overrideName = card.name ??
entityWrapper.displayName;
entityWrapper.unitOfMeasurementOverride = card.unit ??
entityWrapper.unitOfMeasurement;
double fixedValue;
double value = entityWrapper.entity.doubleState;
if (value > card.max) {
fixedValue = card.max.toDouble();
} else if (value < card.min) {
fixedValue = card.min.toDouble();
} else {
fixedValue = value;
}
List<GaugeRange> ranges;
Color currentColor;
if (card.severity != null && card.severity["green"] is int && card.severity["red"] is int && card.severity["yellow"] is int) {
List<RangeContainer> rangesList = <RangeContainer>[
RangeContainer(card.severity["green"], HAClientTheme().getGreenGaugeColor()),
RangeContainer(card.severity["red"], HAClientTheme().getRedGaugeColor()),
RangeContainer(card.severity["yellow"], HAClientTheme().getYellowGaugeColor())
];
rangesList.sort((current, next) {
if (current.startFrom > next.startFrom) {
return 1;
}
if (current.startFrom < next.startFrom) {
return -1;
}
return 0;
});
if (fixedValue < rangesList[1].startFrom) {
currentColor = rangesList[0].color;
} else if (fixedValue < rangesList[2].startFrom && fixedValue >= rangesList[1].startFrom) {
currentColor = rangesList[1].color;
} else {
currentColor = rangesList[2].color;
}
ranges = [
GaugeRange(
startValue: rangesList[0].startFrom.toDouble(),
endValue: rangesList[1].startFrom.toDouble(),
color: rangesList[0].color.withOpacity(0.1),
sizeUnit: GaugeSizeUnit.factor,
endWidth: 0.3,
startWidth: 0.3
),
GaugeRange(
startValue: rangesList[1].startFrom.toDouble(),
endValue: rangesList[2].startFrom.toDouble(),
color: rangesList[1].color.withOpacity(0.1),
sizeUnit: GaugeSizeUnit.factor,
endWidth: 0.3,
startWidth: 0.3
),
GaugeRange(
startValue: rangesList[2].startFrom.toDouble(),
endValue: card.max.toDouble(),
color: rangesList[2].color.withOpacity(0.1),
sizeUnit: GaugeSizeUnit.factor,
endWidth: 0.3,
startWidth: 0.3
)
];
}
if (ranges == null) {
currentColor = Theme.of(context).primaryColorDark;
ranges = <GaugeRange>[
GaugeRange(
startValue: card.min.toDouble(),
endValue: card.max.toDouble(),
color: Theme.of(context).primaryColorDark.withOpacity(0.1),
sizeUnit: GaugeSizeUnit.factor,
endWidth: 0.3,
startWidth: 0.3,
)
];
}
return CardWrapper(
padding: EdgeInsets.all(4),
child: EntityModel(
entityWrapper: entityWrapper,
child: InkWell(
onTap: () => entityWrapper.handleTap(),
onLongPress: () => entityWrapper.handleHold(),
onDoubleTap: () => entityWrapper.handleDoubleTap(),
child: AspectRatio(
aspectRatio: 1.8,
child: Stack(
alignment: Alignment.bottomCenter,
children: <Widget>[
IgnorePointer(
ignoring: true,
child: SfRadialGauge(
axes: <RadialAxis>[
RadialAxis(
maximum: card.max.toDouble(),
minimum: card.min.toDouble(),
showLabels: false,
useRangeColorForAxis: true,
showTicks: false,
canScaleToFit: true,
ranges: ranges,
axisLineStyle: AxisLineStyle(
thickness: 0.3,
thicknessUnit: GaugeSizeUnit.factor,
color: Colors.transparent
),
startAngle: 180,
endAngle: 0,
pointers: <GaugePointer>[
RangePointer(
value: fixedValue,
sizeUnit: GaugeSizeUnit.factor,
width: 0.3,
color: currentColor,
enableAnimation: true,
animationType: AnimationType.bounceOut,
)
]
)
],
)
),
Column(
mainAxisSize: MainAxisSize.max,
crossAxisAlignment: CrossAxisAlignment.center,
children: <Widget>[
Flexible(
flex: 8,
fit: FlexFit.tight,
child: Container()
),
Flexible(
flex: 6,
fit: FlexFit.tight,
child: FractionallySizedBox(
widthFactor: 0.4,
child: FittedBox(
fit: BoxFit.contain,
alignment: Alignment.bottomCenter,
child: SimpleEntityState(
padding: EdgeInsets.all(0),
expanded: false,
maxLines: 1,
textAlign: TextAlign.center
),
)
)
),
Flexible(
flex: 3,
fit: FlexFit.tight,
child: FittedBox(
fit: BoxFit.contain,
child: EntityName(
padding: EdgeInsets.all(0),
textStyle: Theme.of(context).textTheme.subhead
),
)
),
],
)
],
)
),
),
handleTap: true
)
);
}
}
class RangeContainer {
final int startFrom;
Color color;
RangeContainer(this.startFrom, this.color);
}

126
lib/cards/glance_card.dart Normal file
View File

@@ -0,0 +1,126 @@
part of '../main.dart';
class GlanceCard extends StatelessWidget {
final GlanceCardData card;
const GlanceCard({Key key, this.card}) : super(key: key);
@override
Widget build(BuildContext context) {
List<EntityWrapper> entitiesToShow = card.getEntitiesToShow();
if (entitiesToShow.isEmpty && !card.showEmpty) {
return Container(height: 0.0, width: 0.0,);
}
int length = entitiesToShow.length;
int rowsCount;
int columnsCount;
if (length == 0) {
columnsCount = 0;
rowsCount = 0;
} else {
columnsCount = length >= card.columnsCount ? card.columnsCount : entitiesToShow.length;
rowsCount = (length / columnsCount).round();
}
List<TableRow> rows = [];
for (int i = 0; i < rowsCount; i++) {
int start = i*columnsCount;
int end = start + math.min(columnsCount, length - start);
List<Widget> rowChildren = [];
rowChildren.addAll(entitiesToShow.sublist(
start, end
).map(
(EntityWrapper entity){
return Padding(
padding: EdgeInsets.symmetric(vertical: Sizes.rowPadding),
child: EntityModel(
entityWrapper: entity,
child: _buildEntityContainer(context, entity),
handleTap: true
)
);
}
).toList()
);
while (rowChildren.length < columnsCount) {
rowChildren.add(
Container()
);
}
rows.add(
TableRow(
children: rowChildren
)
);
}
return CardWrapper(
child: Center(
child: Padding(
padding: EdgeInsets.only(bottom: Sizes.rowPadding),
child: Column(
mainAxisSize: MainAxisSize.min,
children: <Widget>[
CardHeader(
name: card.title,
emptyPadding: Sizes.rowPadding,
),
Table(
children: rows
)
],
)
)
)
);
}
Widget _buildEntityContainer(BuildContext context, EntityWrapper entityWrapper) {
if (entityWrapper.entity.statelessType == StatelessEntityType.missed) {
return MissedEntityWidget();
} else if (entityWrapper.entity.statelessType != StatelessEntityType.none) {
return Container(width: 0.0, height: 0.0,);
}
List<Widget> result = [];
if (card.showName) {
result.add(_buildName(context));
}
result.add(
EntityIcon(
padding: EdgeInsets.all(0.0),
size: Sizes.iconSize,
)
);
if (card.showState) {
result.add(_buildState());
}
return InkResponse(
child: Column(
mainAxisSize: MainAxisSize.min,
children: result,
),
onTap: () => entityWrapper.handleTap(),
onLongPress: () => entityWrapper.handleHold(),
onDoubleTap: () => entityWrapper.handleDoubleTap(),
);
}
Widget _buildName(BuildContext context) {
return EntityName(
padding: EdgeInsets.only(bottom: Sizes.rowPadding),
textOverflow: TextOverflow.ellipsis,
wordsWrap: false,
textAlign: TextAlign.center,
textStyle: Theme.of(context).textTheme.body1,
);
}
Widget _buildState() {
return SimpleEntityState(
textAlign: TextAlign.center,
expanded: false,
maxLines: 1,
padding: EdgeInsets.only(top: Sizes.rowPadding),
);
}
}

View File

@@ -0,0 +1,30 @@
part of '../main.dart';
class HorizontalStackCard extends StatelessWidget {
final HorizontalStackCardData card;
const HorizontalStackCard({Key key, this.card}) : super(key: key);
@override
Widget build(BuildContext context) {
if (card.childCards.isNotEmpty) {
List<Widget> children = [];
children = card.childCards.map((childCard) => Flexible(
fit: FlexFit.tight,
child: childCard.buildCardWidget()
)
).toList();
return IntrinsicHeight(
child: Row(
mainAxisSize: MainAxisSize.max,
mainAxisAlignment: MainAxisAlignment.center,
crossAxisAlignment: CrossAxisAlignment.stretch,
children: children,
),
);
}
return Container(height: 0.0, width: 0.0,);
}
}

161
lib/cards/light_card.dart Normal file
View File

@@ -0,0 +1,161 @@
part of '../main.dart';
class LightCard extends StatefulWidget {
final LightCardData card;
LightCard({Key key, this.card}) : super(key: key);
@override
State<StatefulWidget> createState() {
return _LightCardState();
}
}
class _LightCardState extends State<LightCard> {
double _actualBrightness;
double _newBrightness;
bool _changedHere = false;
@override
void initState() {
super.initState();
}
void _setBrightness(double value, LightEntity entity) {
setState((){
_newBrightness = value;
_changedHere = true;
});
ConnectionManager().callService(
domain: entity.domain,
service: "turn_on",
entityId: entity.entityId,
data: {"brightness": value.round()}
);
}
@override
Widget build(BuildContext context) {
EntityWrapper entityWrapper = widget.card.entity;
LightEntity entity = entityWrapper.entity;
if (entityWrapper.entity.statelessType == StatelessEntityType.missed) {
return EntityModel(
entityWrapper: widget.card.entity,
child: MissedEntityWidget(),
handleTap: false,
);
}
entityWrapper.overrideName = widget.card.name ??
entityWrapper.displayName;
entityWrapper.overrideIcon = widget.card.icon ??
entityWrapper.icon;
if (!_changedHere) {
_actualBrightness = (entity.brightness ?? 0).toDouble();
_newBrightness = _actualBrightness;
} else {
_changedHere = false;
}
Color lightColor = entity.color?.toColor();
Color color;
if (lightColor != null && lightColor != Colors.white) {
color = lightColor;
} else {
color = Theme.of(context).accentColor;
}
return CardWrapper(
padding: EdgeInsets.all(4),
child: EntityModel(
entityWrapper: entityWrapper,
child: Column(
mainAxisSize: MainAxisSize.min,
crossAxisAlignment: CrossAxisAlignment.center,
children: <Widget>[
ConstrainedBox(
constraints: BoxConstraints.loose(Size(200, 200)),
child: AspectRatio(
aspectRatio: 1,
child: Stack(
alignment: Alignment.center,
children: <Widget>[
SfRadialGauge(
axes: <RadialAxis>[
RadialAxis(
onAxisTapped: (val) {
_setBrightness(val, entity);
},
maximum: 255,
minimum: 0,
showLabels: false,
showTicks: false,
axisLineStyle: AxisLineStyle(
thickness: 0.05,
thicknessUnit: GaugeSizeUnit.factor,
color: HAClientTheme().getDisabledStateColor(context)
),
pointers: <GaugePointer>[
RangePointer(
value: _actualBrightness,
sizeUnit: GaugeSizeUnit.factor,
width: 0.05,
color: color,
enableAnimation: true,
animationType: AnimationType.bounceOut,
),
MarkerPointer(
value: _newBrightness,
markerType: MarkerType.circle,
markerHeight: 20,
markerWidth: 20,
enableDragging: true,
onValueChangeEnd: (val) {
_setBrightness(val, entity);
},
color: HAClientTheme().getColorByEntityState(entity.state, context)
//enableAnimation: true,
//animationType: AnimationType.bounceOut,
)
]
)
],
),
FractionallySizedBox(
heightFactor: 0.4,
widthFactor: 0.4,
child: AspectRatio(
aspectRatio: 1,
child: InkResponse(
onTap: () => entityWrapper.handleTap(),
onLongPress: () => entityWrapper.handleHold(),
child: FittedBox(
fit: BoxFit.contain,
child: EntityIcon(
showBadge: false,
padding: EdgeInsets.all(0)
)
)
)
)
)
],
)
)
),
EntityName(
padding: EdgeInsets.all(0),
wordsWrap: true,
maxLines: 3,
textOverflow: TextOverflow.ellipsis,
textAlign: TextAlign.center,
)
],
),
handleTap: true
)
);
}
}

View File

@@ -0,0 +1,33 @@
part of '../main.dart';
class MarkdownCard extends StatelessWidget {
final MarkdownCardData card;
const MarkdownCard({Key key, this.card}) : super(key: key);
@override
Widget build(BuildContext context) {
if (card.content == null) {
return Container(height: 0.0, width: 0.0,);
} else if (card.content == '***') {
return Container(height: Sizes.rowPadding, width: 0.0,);
}
return CardWrapper(
child: Padding(
padding: EdgeInsets.fromLTRB(Sizes.leftWidgetPadding, Sizes.rowPadding, Sizes.rightWidgetPadding, Sizes.rowPadding),
child: Column(
mainAxisSize: MainAxisSize.min,
crossAxisAlignment: CrossAxisAlignment.stretch,
children: <Widget>[
CardHeader(name: card.title),
MarkdownBody(
data: card.content,
)
],
),
)
);
}
}

View File

@@ -0,0 +1,35 @@
part of '../main.dart';
class MediaControlsCard extends StatelessWidget {
final MediaControlCardData card;
const MediaControlsCard({Key key, this.card}) : super(key: key);
@override
Widget build(BuildContext context) {
if (card.entity.entity.statelessType == StatelessEntityType.missed) {
return EntityModel(
entityWrapper: card.entity,
child: MissedEntityWidget(),
handleTap: false,
);
} else if (card.entity.entity.domain == null || card.entity.entity.domain != 'media_player') {
return EntityModel(
entityWrapper: card.entity,
child: ErrorEntityWidget(
text: '${card.entity.entity?.entityId} is not a media_player',
),
handleTap: false,
);
}
return CardWrapper(
child: EntityModel(
entityWrapper: card.entity,
handleTap: null,
child: MediaPlayerWidget()
)
);
}
}

View File

@@ -0,0 +1,12 @@
part of '../main.dart';
class UnsupportedCard extends StatelessWidget {
final CardData card;
const UnsupportedCard({Key key, this.card}) : super(key: key);
@override
Widget build(BuildContext context) {
return Container();
}
}

View File

@@ -0,0 +1,23 @@
part of '../main.dart';
class VerticalStackCard extends StatelessWidget {
final VerticalStackCardData card;
const VerticalStackCard({Key key, this.card}) : super(key: key);
@override
Widget build(BuildContext context) {
if (card.childCards.isNotEmpty) {
return Column(
mainAxisSize: MainAxisSize.min,
mainAxisAlignment: MainAxisAlignment.start,
children: card.childCards.map<Widget>(
(childCard) => childCard.buildCardWidget()
).toList(),
);
}
return Container(height: 0.0, width: 0.0,);
}
}

View File

@@ -0,0 +1,32 @@
part of '../../main.dart';
class CardHeader extends StatelessWidget {
final String name;
final Widget trailing;
final Widget leading;
final Widget subtitle;
final double emptyPadding;
const CardHeader({Key key, this.name, this.leading, this.emptyPadding: 0, this.trailing, this.subtitle}) : super(key: key);
@override
Widget build(BuildContext context) {
var result;
if ((name != null) && (name.trim().length > 0)) {
result = new ListTile(
trailing: trailing,
leading: leading,
subtitle: subtitle,
title: Text("$name",
textAlign: TextAlign.left,
overflow: TextOverflow.ellipsis,
style: Theme.of(context).textTheme.headline),
);
} else {
result = new Container(width: 0.0, height: emptyPadding);
}
return result;
}
}

View File

@@ -0,0 +1,21 @@
part of '../../main.dart';
class CardWrapper extends StatelessWidget {
final Widget child;
final EdgeInsets padding;
const CardWrapper({Key key, this.child, this.padding: const EdgeInsets.all(0)}) : super(key: key);
@override
Widget build(BuildContext context) {
return Card(
child: Padding(
padding: padding,
child: child
),
);
}
}

87
lib/const.dart Normal file
View File

@@ -0,0 +1,87 @@
part of 'main.dart';
class EntityState {
static const on = 'on';
static const off = 'off';
static const home = 'home';
static const not_home = 'not_home';
static const unknown = 'unknown';
static const open = 'open';
static const opening = 'opening';
static const closed = 'closed';
static const closing = 'closing';
static const playing = 'playing';
static const paused = 'paused';
static const idle = 'idle';
static const standby = 'standby';
static const alarm_disarmed = 'disarmed';
static const alarm_armed_home = 'armed_home';
static const alarm_armed_away = 'armed_away';
static const alarm_armed_night = 'armed_night';
static const alarm_armed_custom_bypass = 'armed_custom_bypass';
static const alarm_pending = 'pending';
static const alarm_arming = 'arming';
static const alarm_disarming = 'disarming';
static const alarm_triggered = 'triggered';
static const locked = 'locked';
static const unlocked = 'unlocked';
static const unavailable = 'unavailable';
static const ok = 'ok';
static const problem = 'problem';
static const active = 'active';
static const cleaning = 'cleaning';
static const docked = 'docked';
static const returning = 'returning';
static const error = 'error';
}
class CardType {
static const HORIZONTAL_STACK = "horizontal-stack";
static const VERTICAL_STACK = "vertical-stack";
static const ENTITIES = "entities";
static const GLANCE = "glance";
static const MEDIA_CONTROL = "media-control";
static const WEATHER_FORECAST = "weather-forecast";
static const THERMOSTAT = "thermostat";
static const SENSOR = "sensor";
static const PLANT_STATUS = "plant-status";
static const PICTURE_ENTITY = "picture-entity";
static const PICTURE_ELEMENTS = "picture-elements";
static const PICTURE = "picture";
static const MAP = "map";
static const IFRAME = "iframe";
static const GAUGE = "gauge";
static const ENTITY_BUTTON = "entity-button";
static const ENTITY = "entity";
static const BUTTON = "button";
static const CONDITIONAL = "conditional";
static const ALARM_PANEL = "alarm-panel";
static const MARKDOWN = "markdown";
static const LIGHT = "light";
static const ENTITY_FILTER = "entity-filter";
static const UNKNOWN = "unknown";
static const HISTORY_GRAPH = "history-graph";
static const PICTURE_GLANCE = "picture-glance";
static const BADGES = "badges";
}
class Sizes {
static const rightWidgetPadding = 10.0;
static const leftWidgetPadding = 10.0;
static const buttonPadding = 4.0;
static const extendedWidgetHeight = 50.0;
static const iconSize = 28.0;
static const largeIconSize = 46.0;
//static const stateFontSize = 15.0;
//static const nameFontSize = 15.0;
//static const smallFontSize = 14.0;
//static const largeFontSize = 24.0;
static const inputWidth = 160.0;
static const rowPadding = 10.0;
static const doubleRowPadding = rowPadding*2;
static const minViewColumnWidth = 350;
static const entityPageMaxWidth = 400.0;
static const mainPageScreenSeparatorWidth = 5.0;
static const tabletMinWidth = minViewColumnWidth + entityPageMaxWidth + 5;
}

View File

@@ -0,0 +1,13 @@
part of '../../main.dart';
class AlarmControlPanelEntity extends Entity {
AlarmControlPanelEntity(Map rawData, String webHost) : super(rawData, webHost);
@override
Widget _buildAdditionalControlsForPage(BuildContext context) {
return AlarmControlPanelControlsWidget(
extended: false,
);
}
}

View File

@@ -0,0 +1,271 @@
part of '../../../main.dart';
class AlarmControlPanelControlsWidget extends StatefulWidget {
final bool extended;
final List states;
const AlarmControlPanelControlsWidget({Key key, @required this.extended, this.states}) : super(key: key);
@override
_AlarmControlPanelControlsWidgetWidgetState createState() => _AlarmControlPanelControlsWidgetWidgetState();
}
class _AlarmControlPanelControlsWidgetWidgetState extends State<AlarmControlPanelControlsWidget> {
String code = "";
List supportedStates;
@override
void initState() {
super.initState();
supportedStates = widget.states ?? ["arm_home", "arm_away"];
}
void _callService(AlarmControlPanelEntity entity, String service) {
ConnectionManager().callService(
domain: entity.domain,
service: service,
entityId: entity.entityId,
data: {"code": "$code"}
);
setState(() {
code = "";
});
}
void _pinPadHandler(value) {
setState(() {
code += "$value";
});
}
void _pinPadClear() {
setState(() {
code = "";
});
}
void _askToTrigger(AlarmControlPanelEntity entity) {
// flutter defined function
showDialog(
context: context,
builder: (BuildContext context) {
// return object of type Dialog
return AlertDialog(
title: new Text("Are you sure?"),
content: new Text("Are you sure want to trigger alarm ${entity.displayName}?"),
actions: <Widget>[
FlatButton(
child: new Text("Yes"),
onPressed: () {
ConnectionManager().callService(
domain: entity.domain,
service: "alarm_trigger",
entityId: entity.entityId
);
Navigator.of(context).pop();
},
),
FlatButton(
child: new Text("No"),
onPressed: () {
Navigator.of(context).pop();
},
),
],
);
},
);
}
@override
Widget build(BuildContext context) {
final entityModel = EntityModel.of(context);
final AlarmControlPanelEntity entity = entityModel.entityWrapper.entity;
List<Widget> buttons = [];
if (entity.state == EntityState.alarm_disarmed) {
if (supportedStates.contains("arm_home")) {
buttons.add(
RaisedButton(
onPressed: () => _callService(entity, "alarm_arm_home"),
child: Text("ARM HOME"),
)
);
}
if (supportedStates.contains("arm_away")) {
buttons.add(
RaisedButton(
onPressed: () => _callService(entity, "alarm_arm_away"),
child: Text("ARM AWAY"),
)
);
}
if (widget.extended) {
if (supportedStates.contains("arm_night")) {
buttons.add(
RaisedButton(
onPressed: () => _callService(entity, "alarm_arm_night"),
child: Text("ARM NIGHT"),
)
);
}
if (supportedStates.contains("arm_custom_bypass")) {
buttons.add(
RaisedButton(
onPressed: () =>
_callService(entity, "alarm_arm_custom_bypass"),
child: Text("ARM CUSTOM BYPASS"),
)
);
}
}
} else {
buttons.add(
RaisedButton(
onPressed: () => _callService(entity, "alarm_disarm"),
child: Text("DISARM"),
)
);
}
Widget pinPad;
if (entity.attributes["code_format"] == null) {
pinPad = Container(width: 0.0, height: 0.0,);
} else {
pinPad = Padding(
padding: EdgeInsets.only(bottom: Sizes.rowPadding),
child: Column(
mainAxisSize: MainAxisSize.min,
children: <Widget>[
Wrap(
spacing: 5.0,
children: <Widget>[
RaisedButton(
onPressed: () => _pinPadHandler("1"),
child: Text("1"),
),
RaisedButton(
onPressed: () => _pinPadHandler("2"),
child: Text("2"),
),
RaisedButton(
onPressed: () => _pinPadHandler("3"),
child: Text("3"),
)
],
),
Wrap(
spacing: 5.0,
children: <Widget>[
RaisedButton(
onPressed: () => _pinPadHandler("4"),
child: Text("4"),
),
RaisedButton(
onPressed: () => _pinPadHandler("5"),
child: Text("5"),
),
RaisedButton(
onPressed: () => _pinPadHandler("6"),
child: Text("6"),
)
],
),
Wrap(
spacing: 5.0,
children: <Widget>[
RaisedButton(
onPressed: () => _pinPadHandler("7"),
child: Text("7"),
),
RaisedButton(
onPressed: () => _pinPadHandler("8"),
child: Text("8"),
),
RaisedButton(
onPressed: () => _pinPadHandler("9"),
child: Text("9"),
)
],
),
Wrap(
spacing: 5.0,
alignment: WrapAlignment.end,
children: <Widget>[
RaisedButton(
onPressed: () => _pinPadHandler("0"),
child: Text("0"),
),
RaisedButton(
onPressed: () => _pinPadClear(),
child: Text("CLEAR"),
)
],
)
],
)
);
}
Widget inputWrapper;
if (entity.attributes["code_format"] == null) {
inputWrapper = Container(width: 0.0, height: 0.0,);
} else {
inputWrapper = Container(
width: 150.0,
child: TextField(
decoration: InputDecoration(
labelText: "Alarm Code"
),
//focusNode: _focusNode,
obscureText: true,
controller: new TextEditingController.fromValue(
new TextEditingValue(
text: code,
selection:
new TextSelection.collapsed(offset: code.length)
)
),
onChanged: (value) {
code = value;
}
)
);
}
Widget buttonsWrapper = Padding(
padding: EdgeInsets.symmetric(vertical: Sizes.rowPadding),
child: Wrap(
alignment: WrapAlignment.center,
spacing: 15.0,
runSpacing: Sizes.rowPadding,
children: buttons
)
);
Widget triggerButton = Row(
mainAxisAlignment: MainAxisAlignment.end,
children: [
FlatButton(
child: Text(
"TRIGGER",
style: Theme.of(context).textTheme.subhead.copyWith(
color: Theme.of(context).errorColor
)
),
onPressed: () => _askToTrigger(entity),
)
]
);
return Column(
crossAxisAlignment: CrossAxisAlignment.center,
children: <Widget>[
widget.extended ? buttonsWrapper : inputWrapper,
widget.extended ? inputWrapper : buttonsWrapper,
widget.extended ? pinPad : triggerButton
]
);
}
}

View File

@@ -0,0 +1,27 @@
part of '../../main.dart';
class AutomationEntity extends Entity {
AutomationEntity(Map rawData, String webHost) : super(rawData, webHost);
@override
Widget _buildStatePart(BuildContext context) {
return SwitchStateWidget();
}
@override
Widget _buildAdditionalControlsForPage(BuildContext context) {
return Row(
mainAxisAlignment: MainAxisAlignment.center,
mainAxisSize: MainAxisSize.max,
children: <Widget>[
FlatServiceButton(
serviceDomain: domain,
entityId: entityId,
text: "TRIGGER",
serviceName: "trigger",
)
],
);
}
}

View File

@@ -0,0 +1,16 @@
part of '../../main.dart';
class ButtonEntity extends Entity {
ButtonEntity(Map rawData, String webHost) : super(rawData, webHost);
@override
Widget _buildStatePart(BuildContext context) {
return FlatServiceButton(
entityId: entityId,
serviceDomain: domain,
serviceName: 'turn_on',
text: domain == "scene" ? "ACTIVATE" : "EXECUTE",
);
}
}

View File

@@ -0,0 +1,21 @@
part of '../../main.dart';
class CameraEntity extends Entity {
static const SUPPORT_ON_OFF = 1;
static const SUPPORT_STREAM = 2;
CameraEntity(Map rawData, String webHost) : super(rawData, webHost);
bool get supportOnOff => ((supportedFeatures &
CameraEntity.SUPPORT_ON_OFF) ==
CameraEntity.SUPPORT_ON_OFF);
bool get supportStream => ((supportedFeatures &
CameraEntity.SUPPORT_STREAM) ==
CameraEntity.SUPPORT_STREAM);
@override
Widget _buildAdditionalControlsForPage(BuildContext context) {
return CameraStreamView();
}
}

Some files were not shown because too many files have changed in this diff Show More