Compare commits
56 Commits
Author | SHA1 | Date | |
---|---|---|---|
4b6dda5a9c | |||
4099fa0c83 | |||
76057e8797 | |||
538d3603dc | |||
bc0e72ca52 | |||
f25a47beb2 | |||
cc3c6b0087 | |||
6cf80c0bfd | |||
8ce9bdb7a5 | |||
31e50150b1 | |||
e359150d97 | |||
93680c981c | |||
e06b66c523 | |||
3dea844e1e | |||
62b1af30e0 | |||
e006c4e403 | |||
983573388e | |||
bdd1dc7e17 | |||
9c1970ee14 | |||
d0e0bf3571 | |||
b399357517 | |||
0290cd3a32 | |||
d8a1d03179 | |||
216fad3cb9 | |||
fead6ea348 | |||
8814687be6 | |||
71c0e2caa0 | |||
1531c41542 | |||
bc90d013e8 | |||
2adfaca0c4 | |||
6cc1a37d9d | |||
4bb616b327 | |||
38219618ba | |||
6774b53758 | |||
29a94c882f | |||
5897fa3a99 | |||
7af92c2dc9 | |||
1094177a42 | |||
5e814e8109 | |||
24c7675fa4 | |||
dc3ca38c78 | |||
96b528e055 | |||
3858036631 | |||
19d42ceeb3 | |||
a2836a3603 | |||
2a45758a6d | |||
dc1bf4d878 | |||
e82ba60c4e | |||
09199d30e8 | |||
724d32dbe2 | |||
949c8ee44e | |||
1a446d34c7 | |||
22a5847285 | |||
1c8f770f10 | |||
be5ea55f6b | |||
c65ade9827 |
76
CODE_OF_CONDUCT.md
Normal file
76
CODE_OF_CONDUCT.md
Normal 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
|
@ -4,7 +4,7 @@
|
|||||||
|
|
||||||
Home Assistant Android client on Dart with Flutter.
|
Home Assistant Android client on Dart with Flutter.
|
||||||
|
|
||||||
Visit [www.keyboardcrumbs.io](http://www.keyboardcrumbs.io/ha-client) for more info.
|
Visit [www.vynn.co](https://www.vynn.co/ha-client) for more info.
|
||||||
|
|
||||||
Join [Google Group](https://groups.google.com/d/forum/ha-client-alpha-testing) to become an alpha tester
|
Join [Google Group](https://groups.google.com/d/forum/ha-client-alpha-testing) to become an alpha tester
|
||||||
|
|
||||||
|
@ -31,6 +31,11 @@ keystoreProperties.load(new FileInputStream(keystorePropertiesFile))
|
|||||||
android {
|
android {
|
||||||
compileSdkVersion 27
|
compileSdkVersion 27
|
||||||
|
|
||||||
|
compileOptions {
|
||||||
|
sourceCompatibility JavaVersion.VERSION_1_8
|
||||||
|
targetCompatibility JavaVersion.VERSION_1_8
|
||||||
|
}
|
||||||
|
|
||||||
lintOptions {
|
lintOptions {
|
||||||
disable 'InvalidPackage'
|
disable 'InvalidPackage'
|
||||||
}
|
}
|
||||||
|
105
lib/configuration.page.dart
Normal file
105
lib/configuration.page.dart
Normal file
@ -0,0 +1,105 @@
|
|||||||
|
part of 'main.dart';
|
||||||
|
|
||||||
|
class ConfigurationPage extends StatefulWidget {
|
||||||
|
ConfigurationPage({Key key, this.title}) : super(key: key);
|
||||||
|
|
||||||
|
final String title;
|
||||||
|
|
||||||
|
@override
|
||||||
|
_ConfigurationPageState createState() => new _ConfigurationPageState();
|
||||||
|
}
|
||||||
|
|
||||||
|
class ConfigurationItem {
|
||||||
|
ConfigurationItem({ this.isExpanded: false, this.header, this.body });
|
||||||
|
|
||||||
|
bool isExpanded;
|
||||||
|
final String header;
|
||||||
|
final Widget body;
|
||||||
|
}
|
||||||
|
|
||||||
|
class _ConfigurationPageState extends State<ConfigurationPage> {
|
||||||
|
|
||||||
|
List<ConfigurationItem> _items;
|
||||||
|
|
||||||
|
@override
|
||||||
|
void initState() {
|
||||||
|
super.initState();
|
||||||
|
_items = <ConfigurationItem>[
|
||||||
|
ConfigurationItem(
|
||||||
|
header: 'General',
|
||||||
|
body: Padding(
|
||||||
|
padding: EdgeInsets.fromLTRB(Sizes.leftWidgetPadding, 0.0, Sizes.rightWidgetPadding, Sizes.rowPadding),
|
||||||
|
child: Column(
|
||||||
|
crossAxisAlignment: CrossAxisAlignment.start,
|
||||||
|
mainAxisSize: MainAxisSize.min,
|
||||||
|
children: <Widget>[
|
||||||
|
Text("Server management", style: TextStyle(fontSize: Sizes.largeFontSize)),
|
||||||
|
Container(height: Sizes.rowPadding,),
|
||||||
|
Text("Control your Home Assistant server from HA Client."),
|
||||||
|
Divider(),
|
||||||
|
Row(
|
||||||
|
mainAxisSize: MainAxisSize.min,
|
||||||
|
children: <Widget>[
|
||||||
|
FlatServiceButton(
|
||||||
|
text: "Restart",
|
||||||
|
serviceName: "restart",
|
||||||
|
serviceDomain: "homeassistant",
|
||||||
|
entityId: null,
|
||||||
|
),
|
||||||
|
FlatServiceButton(
|
||||||
|
text: "Stop",
|
||||||
|
serviceName: "stop",
|
||||||
|
serviceDomain: "homeassistant",
|
||||||
|
entityId: null,
|
||||||
|
),
|
||||||
|
],
|
||||||
|
)
|
||||||
|
],
|
||||||
|
),
|
||||||
|
)
|
||||||
|
)
|
||||||
|
];
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
Widget build(BuildContext context) {
|
||||||
|
|
||||||
|
return new Scaffold(
|
||||||
|
appBar: new AppBar(
|
||||||
|
leading: IconButton(icon: Icon(Icons.arrow_back), onPressed: (){
|
||||||
|
Navigator.pop(context);
|
||||||
|
}),
|
||||||
|
title: new Text(widget.title),
|
||||||
|
),
|
||||||
|
body: ListView(
|
||||||
|
children: [
|
||||||
|
new ExpansionPanelList(
|
||||||
|
expansionCallback: (int index, bool isExpanded) {
|
||||||
|
setState(() {
|
||||||
|
_items[index].isExpanded = !_items[index].isExpanded;
|
||||||
|
});
|
||||||
|
},
|
||||||
|
children: _items.map((ConfigurationItem item) {
|
||||||
|
return new ExpansionPanel(
|
||||||
|
headerBuilder: (BuildContext context, bool isExpanded) {
|
||||||
|
return CardHeaderWidget(
|
||||||
|
name: item.header,
|
||||||
|
);
|
||||||
|
},
|
||||||
|
isExpanded: item.isExpanded,
|
||||||
|
body: new Container(
|
||||||
|
child: item.body,
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}).toList(),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
void dispose() {
|
||||||
|
super.dispose();
|
||||||
|
}
|
||||||
|
}
|
@ -19,8 +19,8 @@ class _EntityViewPageState extends State<EntityViewPage> {
|
|||||||
void initState() {
|
void initState() {
|
||||||
super.initState();
|
super.initState();
|
||||||
_stateSubscription = eventBus.on<StateChangedEvent>().listen((event) {
|
_stateSubscription = eventBus.on<StateChangedEvent>().listen((event) {
|
||||||
Logger.d("State change event handled by entity page: ${event.entityId}");
|
|
||||||
if (event.entityId == widget.entityId) {
|
if (event.entityId == widget.entityId) {
|
||||||
|
Logger.d("State change event handled by entity page: ${event.entityId}");
|
||||||
setState(() {});
|
setState(() {});
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
@ -46,12 +46,9 @@ class _EntityViewPageState extends State<EntityViewPage> {
|
|||||||
// the App.build method, and use it to set our appbar title.
|
// the App.build method, and use it to set our appbar title.
|
||||||
title: new Text(_title),
|
title: new Text(_title),
|
||||||
),
|
),
|
||||||
body: Padding(
|
body: HomeAssistantModel(
|
||||||
padding: EdgeInsets.all(10.0),
|
|
||||||
child: HomeAssistantModel(
|
|
||||||
homeAssistant: widget.homeAssistant,
|
homeAssistant: widget.homeAssistant,
|
||||||
child: widget.homeAssistant.entities.get(widget.entityId).buildEntityPageWidget(context)
|
child: widget.homeAssistant.entities.get(widget.entityId).buildEntityPageWidget(context)
|
||||||
)
|
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
12
lib/entity_class/alarm_control_panel.class.dart
Normal file
12
lib/entity_class/alarm_control_panel.class.dart
Normal file
@ -0,0 +1,12 @@
|
|||||||
|
part of '../main.dart';
|
||||||
|
|
||||||
|
class AlarmControlPanelEntity extends Entity {
|
||||||
|
AlarmControlPanelEntity(Map rawData) : super(rawData);
|
||||||
|
|
||||||
|
@override
|
||||||
|
Widget _buildAdditionalControlsForPage(BuildContext context) {
|
||||||
|
return AlarmControlPanelControlsWidget(
|
||||||
|
extended: false,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
26
lib/entity_class/automation_entity.dart
Normal file
26
lib/entity_class/automation_entity.dart
Normal file
@ -0,0 +1,26 @@
|
|||||||
|
part of '../main.dart';
|
||||||
|
|
||||||
|
class AutomationEntity extends Entity {
|
||||||
|
AutomationEntity(Map rawData) : super(rawData);
|
||||||
|
|
||||||
|
@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",
|
||||||
|
)
|
||||||
|
],
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
@ -5,6 +5,11 @@ class ButtonEntity extends Entity {
|
|||||||
|
|
||||||
@override
|
@override
|
||||||
Widget _buildStatePart(BuildContext context) {
|
Widget _buildStatePart(BuildContext context) {
|
||||||
return ButtonStateWidget();
|
return FlatServiceButton(
|
||||||
|
entityId: entityId,
|
||||||
|
serviceDomain: domain,
|
||||||
|
serviceName: 'turn_on',
|
||||||
|
text: "EXECUTE",
|
||||||
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
17
lib/entity_class/camera_entity.class.dart
Normal file
17
lib/entity_class/camera_entity.class.dart
Normal file
@ -0,0 +1,17 @@
|
|||||||
|
part of '../main.dart';
|
||||||
|
|
||||||
|
class CameraEntity extends Entity {
|
||||||
|
|
||||||
|
static const SUPPORT_ON_OFF = 1;
|
||||||
|
|
||||||
|
CameraEntity(Map rawData) : super(rawData);
|
||||||
|
|
||||||
|
bool get supportOnOff => ((supportedFeatures &
|
||||||
|
CameraEntity.SUPPORT_ON_OFF) ==
|
||||||
|
CameraEntity.SUPPORT_ON_OFF);
|
||||||
|
|
||||||
|
@override
|
||||||
|
Widget _buildAdditionalControlsForPage(BuildContext context) {
|
||||||
|
return CameraStreamView();
|
||||||
|
}
|
||||||
|
}
|
@ -23,44 +23,44 @@ class ClimateEntity extends Entity {
|
|||||||
static const SUPPORT_AUX_HEAT = 2048;
|
static const SUPPORT_AUX_HEAT = 2048;
|
||||||
static const SUPPORT_ON_OFF = 4096;
|
static const SUPPORT_ON_OFF = 4096;
|
||||||
|
|
||||||
bool get supportTargetTemperature => ((attributes["supported_features"] &
|
bool get supportTargetTemperature => ((supportedFeatures &
|
||||||
ClimateEntity.SUPPORT_TARGET_TEMPERATURE) ==
|
ClimateEntity.SUPPORT_TARGET_TEMPERATURE) ==
|
||||||
ClimateEntity.SUPPORT_TARGET_TEMPERATURE);
|
ClimateEntity.SUPPORT_TARGET_TEMPERATURE);
|
||||||
bool get supportTargetTemperatureHigh => ((attributes["supported_features"] &
|
bool get supportTargetTemperatureHigh => ((supportedFeatures &
|
||||||
ClimateEntity.SUPPORT_TARGET_TEMPERATURE_HIGH) ==
|
ClimateEntity.SUPPORT_TARGET_TEMPERATURE_HIGH) ==
|
||||||
ClimateEntity.SUPPORT_TARGET_TEMPERATURE_HIGH);
|
ClimateEntity.SUPPORT_TARGET_TEMPERATURE_HIGH);
|
||||||
bool get supportTargetTemperatureLow => ((attributes["supported_features"] &
|
bool get supportTargetTemperatureLow => ((supportedFeatures &
|
||||||
ClimateEntity.SUPPORT_TARGET_TEMPERATURE_LOW) ==
|
ClimateEntity.SUPPORT_TARGET_TEMPERATURE_LOW) ==
|
||||||
ClimateEntity.SUPPORT_TARGET_TEMPERATURE_LOW);
|
ClimateEntity.SUPPORT_TARGET_TEMPERATURE_LOW);
|
||||||
bool get supportTargetHumidity => ((attributes["supported_features"] &
|
bool get supportTargetHumidity => ((supportedFeatures &
|
||||||
ClimateEntity.SUPPORT_TARGET_HUMIDITY) ==
|
ClimateEntity.SUPPORT_TARGET_HUMIDITY) ==
|
||||||
ClimateEntity.SUPPORT_TARGET_HUMIDITY);
|
ClimateEntity.SUPPORT_TARGET_HUMIDITY);
|
||||||
bool get supportTargetHumidityHigh => ((attributes["supported_features"] &
|
bool get supportTargetHumidityHigh => ((supportedFeatures &
|
||||||
ClimateEntity.SUPPORT_TARGET_HUMIDITY_HIGH) ==
|
ClimateEntity.SUPPORT_TARGET_HUMIDITY_HIGH) ==
|
||||||
ClimateEntity.SUPPORT_TARGET_HUMIDITY_HIGH);
|
ClimateEntity.SUPPORT_TARGET_HUMIDITY_HIGH);
|
||||||
bool get supportTargetHumidityLow => ((attributes["supported_features"] &
|
bool get supportTargetHumidityLow => ((supportedFeatures &
|
||||||
ClimateEntity.SUPPORT_TARGET_HUMIDITY_LOW) ==
|
ClimateEntity.SUPPORT_TARGET_HUMIDITY_LOW) ==
|
||||||
ClimateEntity.SUPPORT_TARGET_HUMIDITY_LOW);
|
ClimateEntity.SUPPORT_TARGET_HUMIDITY_LOW);
|
||||||
bool get supportFanMode =>
|
bool get supportFanMode =>
|
||||||
((attributes["supported_features"] & ClimateEntity.SUPPORT_FAN_MODE) ==
|
((supportedFeatures & ClimateEntity.SUPPORT_FAN_MODE) ==
|
||||||
ClimateEntity.SUPPORT_FAN_MODE);
|
ClimateEntity.SUPPORT_FAN_MODE);
|
||||||
bool get supportOperationMode => ((attributes["supported_features"] &
|
bool get supportOperationMode => ((supportedFeatures &
|
||||||
ClimateEntity.SUPPORT_OPERATION_MODE) ==
|
ClimateEntity.SUPPORT_OPERATION_MODE) ==
|
||||||
ClimateEntity.SUPPORT_OPERATION_MODE);
|
ClimateEntity.SUPPORT_OPERATION_MODE);
|
||||||
bool get supportHoldMode =>
|
bool get supportHoldMode =>
|
||||||
((attributes["supported_features"] & ClimateEntity.SUPPORT_HOLD_MODE) ==
|
((supportedFeatures & ClimateEntity.SUPPORT_HOLD_MODE) ==
|
||||||
ClimateEntity.SUPPORT_HOLD_MODE);
|
ClimateEntity.SUPPORT_HOLD_MODE);
|
||||||
bool get supportSwingMode =>
|
bool get supportSwingMode =>
|
||||||
((attributes["supported_features"] & ClimateEntity.SUPPORT_SWING_MODE) ==
|
((supportedFeatures & ClimateEntity.SUPPORT_SWING_MODE) ==
|
||||||
ClimateEntity.SUPPORT_SWING_MODE);
|
ClimateEntity.SUPPORT_SWING_MODE);
|
||||||
bool get supportAwayMode =>
|
bool get supportAwayMode =>
|
||||||
((attributes["supported_features"] & ClimateEntity.SUPPORT_AWAY_MODE) ==
|
((supportedFeatures & ClimateEntity.SUPPORT_AWAY_MODE) ==
|
||||||
ClimateEntity.SUPPORT_AWAY_MODE);
|
ClimateEntity.SUPPORT_AWAY_MODE);
|
||||||
bool get supportAuxHeat =>
|
bool get supportAuxHeat =>
|
||||||
((attributes["supported_features"] & ClimateEntity.SUPPORT_AUX_HEAT) ==
|
((supportedFeatures & ClimateEntity.SUPPORT_AUX_HEAT) ==
|
||||||
ClimateEntity.SUPPORT_AUX_HEAT);
|
ClimateEntity.SUPPORT_AUX_HEAT);
|
||||||
bool get supportOnOff =>
|
bool get supportOnOff =>
|
||||||
((attributes["supported_features"] & ClimateEntity.SUPPORT_ON_OFF) ==
|
((supportedFeatures & ClimateEntity.SUPPORT_ON_OFF) ==
|
||||||
ClimateEntity.SUPPORT_ON_OFF);
|
ClimateEntity.SUPPORT_ON_OFF);
|
||||||
|
|
||||||
List<String> get operationList => attributes["operation_list"] != null
|
List<String> get operationList => attributes["operation_list"] != null
|
||||||
@ -80,6 +80,7 @@ class ClimateEntity extends Entity {
|
|||||||
double get targetHumidity => _getDoubleAttributeValue('humidity');
|
double get targetHumidity => _getDoubleAttributeValue('humidity');
|
||||||
double get maxHumidity => _getDoubleAttributeValue('max_humidity');
|
double get maxHumidity => _getDoubleAttributeValue('max_humidity');
|
||||||
double get minHumidity => _getDoubleAttributeValue('min_humidity');
|
double get minHumidity => _getDoubleAttributeValue('min_humidity');
|
||||||
|
double get temperatureStep => _getDoubleAttributeValue('target_temp_step') ?? 0.5;
|
||||||
String get operationMode => attributes['operation_mode'];
|
String get operationMode => attributes['operation_mode'];
|
||||||
String get fanMode => attributes['fan_mode'];
|
String get fanMode => attributes['fan_mode'];
|
||||||
String get swingMode => attributes['swing_mode'];
|
String get swingMode => attributes['swing_mode'];
|
||||||
|
@ -94,4 +94,5 @@ class CardType {
|
|||||||
static const entityButton = "entity-button";
|
static const entityButton = "entity-button";
|
||||||
static const conditional = "conditional";
|
static const conditional = "conditional";
|
||||||
static const alarmPanel = "alarm-panel";
|
static const alarmPanel = "alarm-panel";
|
||||||
|
static const markdown = "markdown";
|
||||||
}
|
}
|
@ -11,29 +11,29 @@ class CoverEntity extends Entity {
|
|||||||
static const SUPPORT_STOP_TILT = 64;
|
static const SUPPORT_STOP_TILT = 64;
|
||||||
static const SUPPORT_SET_TILT_POSITION = 128;
|
static const SUPPORT_SET_TILT_POSITION = 128;
|
||||||
|
|
||||||
bool get supportOpen => ((attributes["supported_features"] &
|
bool get supportOpen => ((supportedFeatures &
|
||||||
CoverEntity.SUPPORT_OPEN) ==
|
CoverEntity.SUPPORT_OPEN) ==
|
||||||
CoverEntity.SUPPORT_OPEN);
|
CoverEntity.SUPPORT_OPEN);
|
||||||
bool get supportClose => ((attributes["supported_features"] &
|
bool get supportClose => ((supportedFeatures &
|
||||||
CoverEntity.SUPPORT_CLOSE) ==
|
CoverEntity.SUPPORT_CLOSE) ==
|
||||||
CoverEntity.SUPPORT_CLOSE);
|
CoverEntity.SUPPORT_CLOSE);
|
||||||
bool get supportSetPosition => ((attributes["supported_features"] &
|
bool get supportSetPosition => ((supportedFeatures &
|
||||||
CoverEntity.SUPPORT_SET_POSITION) ==
|
CoverEntity.SUPPORT_SET_POSITION) ==
|
||||||
CoverEntity.SUPPORT_SET_POSITION);
|
CoverEntity.SUPPORT_SET_POSITION);
|
||||||
bool get supportStop => ((attributes["supported_features"] &
|
bool get supportStop => ((supportedFeatures &
|
||||||
CoverEntity.SUPPORT_STOP) ==
|
CoverEntity.SUPPORT_STOP) ==
|
||||||
CoverEntity.SUPPORT_STOP);
|
CoverEntity.SUPPORT_STOP);
|
||||||
|
|
||||||
bool get supportOpenTilt => ((attributes["supported_features"] &
|
bool get supportOpenTilt => ((supportedFeatures &
|
||||||
CoverEntity.SUPPORT_OPEN_TILT) ==
|
CoverEntity.SUPPORT_OPEN_TILT) ==
|
||||||
CoverEntity.SUPPORT_OPEN_TILT);
|
CoverEntity.SUPPORT_OPEN_TILT);
|
||||||
bool get supportCloseTilt => ((attributes["supported_features"] &
|
bool get supportCloseTilt => ((supportedFeatures &
|
||||||
CoverEntity.SUPPORT_CLOSE_TILT) ==
|
CoverEntity.SUPPORT_CLOSE_TILT) ==
|
||||||
CoverEntity.SUPPORT_CLOSE_TILT);
|
CoverEntity.SUPPORT_CLOSE_TILT);
|
||||||
bool get supportStopTilt => ((attributes["supported_features"] &
|
bool get supportStopTilt => ((supportedFeatures &
|
||||||
CoverEntity.SUPPORT_STOP_TILT) ==
|
CoverEntity.SUPPORT_STOP_TILT) ==
|
||||||
CoverEntity.SUPPORT_STOP_TILT);
|
CoverEntity.SUPPORT_STOP_TILT);
|
||||||
bool get supportSetTiltPosition => ((attributes["supported_features"] &
|
bool get supportSetTiltPosition => ((supportedFeatures &
|
||||||
CoverEntity.SUPPORT_SET_TILT_POSITION) ==
|
CoverEntity.SUPPORT_SET_TILT_POSITION) ==
|
||||||
CoverEntity.SUPPORT_SET_TILT_POSITION);
|
CoverEntity.SUPPORT_SET_TILT_POSITION);
|
||||||
|
|
||||||
|
@ -34,7 +34,7 @@ class Entity {
|
|||||||
"moisture.on": "Wet",
|
"moisture.on": "Wet",
|
||||||
"moisture.off": "Dry",
|
"moisture.off": "Dry",
|
||||||
"motion.on": "Detected",
|
"motion.on": "Detected",
|
||||||
"motion.off": "Cler",
|
"motion.off": "Clear",
|
||||||
"moving.on": "Moving",
|
"moving.on": "Moving",
|
||||||
"moving.off": "Stopped",
|
"moving.off": "Stopped",
|
||||||
"occupancy.on": "Occupied",
|
"occupancy.on": "Occupied",
|
||||||
@ -91,6 +91,7 @@ class Entity {
|
|||||||
String get lastUpdated => _getLastUpdatedFormatted();
|
String get lastUpdated => _getLastUpdatedFormatted();
|
||||||
bool get isHidden => attributes["hidden"] ?? false;
|
bool get isHidden => attributes["hidden"] ?? false;
|
||||||
double get doubleState => double.tryParse(state) ?? 0.0;
|
double get doubleState => double.tryParse(state) ?? 0.0;
|
||||||
|
int get supportedFeatures => attributes["supported_features"] ?? 0;
|
||||||
|
|
||||||
Entity(Map rawData) {
|
Entity(Map rawData) {
|
||||||
update(rawData);
|
update(rawData);
|
||||||
@ -162,7 +163,10 @@ class Entity {
|
|||||||
return EntityModel(
|
return EntityModel(
|
||||||
entityWrapper: EntityWrapper(entity: this),
|
entityWrapper: EntityWrapper(entity: this),
|
||||||
child: EntityPageContainer(children: <Widget>[
|
child: EntityPageContainer(children: <Widget>[
|
||||||
DefaultEntityContainer(state: _buildStatePartForPage(context)),
|
Padding(
|
||||||
|
padding: EdgeInsets.only(top: Sizes.rowPadding),
|
||||||
|
child: DefaultEntityContainer(state: _buildStatePartForPage(context)),
|
||||||
|
),
|
||||||
LastUpdatedWidget(),
|
LastUpdatedWidget(),
|
||||||
Divider(),
|
Divider(),
|
||||||
_buildAdditionalControlsForPage(context),
|
_buildAdditionalControlsForPage(context),
|
||||||
|
@ -8,13 +8,13 @@ class FanEntity extends Entity {
|
|||||||
|
|
||||||
FanEntity(Map rawData) : super(rawData);
|
FanEntity(Map rawData) : super(rawData);
|
||||||
|
|
||||||
bool get supportSetSpeed => ((attributes["supported_features"] &
|
bool get supportSetSpeed => ((supportedFeatures &
|
||||||
FanEntity.SUPPORT_SET_SPEED) ==
|
FanEntity.SUPPORT_SET_SPEED) ==
|
||||||
FanEntity.SUPPORT_SET_SPEED);
|
FanEntity.SUPPORT_SET_SPEED);
|
||||||
bool get supportOscillate => ((attributes["supported_features"] &
|
bool get supportOscillate => ((supportedFeatures &
|
||||||
FanEntity.SUPPORT_OSCILLATE) ==
|
FanEntity.SUPPORT_OSCILLATE) ==
|
||||||
FanEntity.SUPPORT_OSCILLATE);
|
FanEntity.SUPPORT_OSCILLATE);
|
||||||
bool get supportDirection => ((attributes["supported_features"] &
|
bool get supportDirection => ((supportedFeatures &
|
||||||
FanEntity.SUPPORT_DIRECTION) ==
|
FanEntity.SUPPORT_DIRECTION) ==
|
||||||
FanEntity.SUPPORT_DIRECTION);
|
FanEntity.SUPPORT_DIRECTION);
|
||||||
|
|
||||||
|
@ -10,43 +10,46 @@ class LightEntity extends Entity {
|
|||||||
static const SUPPORT_TRANSITION = 32;
|
static const SUPPORT_TRANSITION = 32;
|
||||||
static const SUPPORT_WHITE_VALUE = 128;
|
static const SUPPORT_WHITE_VALUE = 128;
|
||||||
|
|
||||||
bool get supportBrightness => ((attributes["supported_features"] &
|
bool get supportBrightness => ((supportedFeatures &
|
||||||
LightEntity.SUPPORT_BRIGHTNESS) ==
|
LightEntity.SUPPORT_BRIGHTNESS) ==
|
||||||
LightEntity.SUPPORT_BRIGHTNESS);
|
LightEntity.SUPPORT_BRIGHTNESS);
|
||||||
bool get supportColorTemp => ((attributes["supported_features"] &
|
bool get supportColorTemp => ((supportedFeatures &
|
||||||
LightEntity.SUPPORT_COLOR_TEMP) ==
|
LightEntity.SUPPORT_COLOR_TEMP) ==
|
||||||
LightEntity.SUPPORT_COLOR_TEMP);
|
LightEntity.SUPPORT_COLOR_TEMP);
|
||||||
bool get supportEffect => ((attributes["supported_features"] &
|
bool get supportEffect => ((supportedFeatures &
|
||||||
LightEntity.SUPPORT_EFFECT) ==
|
LightEntity.SUPPORT_EFFECT) ==
|
||||||
LightEntity.SUPPORT_EFFECT);
|
LightEntity.SUPPORT_EFFECT);
|
||||||
bool get supportFlash => ((attributes["supported_features"] &
|
bool get supportFlash => ((supportedFeatures &
|
||||||
LightEntity.SUPPORT_FLASH) ==
|
LightEntity.SUPPORT_FLASH) ==
|
||||||
LightEntity.SUPPORT_FLASH);
|
LightEntity.SUPPORT_FLASH);
|
||||||
bool get supportColor => ((attributes["supported_features"] &
|
bool get supportColor => ((supportedFeatures &
|
||||||
LightEntity.SUPPORT_COLOR) ==
|
LightEntity.SUPPORT_COLOR) ==
|
||||||
LightEntity.SUPPORT_COLOR);
|
LightEntity.SUPPORT_COLOR);
|
||||||
bool get supportTransition => ((attributes["supported_features"] &
|
bool get supportTransition => ((supportedFeatures &
|
||||||
LightEntity.SUPPORT_TRANSITION) ==
|
LightEntity.SUPPORT_TRANSITION) ==
|
||||||
LightEntity.SUPPORT_TRANSITION);
|
LightEntity.SUPPORT_TRANSITION);
|
||||||
bool get supportWhiteValue => ((attributes["supported_features"] &
|
bool get supportWhiteValue => ((supportedFeatures &
|
||||||
LightEntity.SUPPORT_WHITE_VALUE) ==
|
LightEntity.SUPPORT_WHITE_VALUE) ==
|
||||||
LightEntity.SUPPORT_WHITE_VALUE);
|
LightEntity.SUPPORT_WHITE_VALUE);
|
||||||
|
|
||||||
int get brightness => _getIntAttributeValue("brightness");
|
int get brightness => _getIntAttributeValue("brightness");
|
||||||
|
String get effect => attributes["effect"];
|
||||||
int get colorTemp => _getIntAttributeValue("color_temp");
|
int get colorTemp => _getIntAttributeValue("color_temp");
|
||||||
double get maxMireds => _getDoubleAttributeValue("max_mireds");
|
double get maxMireds => _getDoubleAttributeValue("max_mireds");
|
||||||
double get minMireds => _getDoubleAttributeValue("min_mireds");
|
double get minMireds => _getDoubleAttributeValue("min_mireds");
|
||||||
Color get color => _getColor();
|
HSVColor get color => _getColor();
|
||||||
bool get isAdditionalControls => ((attributes["supported_features"] != null) && (attributes["supported_features"] != 0));
|
bool get isAdditionalControls => ((supportedFeatures != null) && (supportedFeatures != 0));
|
||||||
List<String> get effectList => getStringListAttributeValue("effect_list");
|
List<String> get effectList => getStringListAttributeValue("effect_list");
|
||||||
|
|
||||||
LightEntity(Map rawData) : super(rawData);
|
LightEntity(Map rawData) : super(rawData);
|
||||||
|
|
||||||
Color _getColor() {
|
HSVColor _getColor() {
|
||||||
List rgb = attributes["rgb_color"];
|
List hs = attributes["hs_color"];
|
||||||
try {
|
try {
|
||||||
if ((rgb != null) && (rgb.length > 0)) {
|
if ((hs != null) && (hs.length > 0)) {
|
||||||
return Color.fromARGB(255, rgb[0], rgb[1], rgb[2]);
|
double sat = hs[1]/100;
|
||||||
|
String ssat = sat.toStringAsFixed(2);
|
||||||
|
return HSVColor.fromAHSV(1.0, hs[0], double.parse(ssat), 1.0);
|
||||||
} else {
|
} else {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
@ -62,7 +65,7 @@ class LightEntity extends Entity {
|
|||||||
|
|
||||||
@override
|
@override
|
||||||
Widget _buildAdditionalControlsForPage(BuildContext context) {
|
Widget _buildAdditionalControlsForPage(BuildContext context) {
|
||||||
if (!isAdditionalControls) {
|
if (!isAdditionalControls || state == EntityState.unavailable) {
|
||||||
return Container(height: 0.0, width: 0.0);
|
return Container(height: 0.0, width: 0.0);
|
||||||
} else {
|
} else {
|
||||||
return LightControlsWidget();
|
return LightControlsWidget();
|
||||||
|
@ -22,53 +22,53 @@ class MediaPlayerEntity extends Entity {
|
|||||||
|
|
||||||
MediaPlayerEntity(Map rawData) : super(rawData);
|
MediaPlayerEntity(Map rawData) : super(rawData);
|
||||||
|
|
||||||
bool get supportPause => ((attributes["supported_features"] &
|
bool get supportPause => ((supportedFeatures &
|
||||||
MediaPlayerEntity.SUPPORT_PAUSE) ==
|
MediaPlayerEntity.SUPPORT_PAUSE) ==
|
||||||
MediaPlayerEntity.SUPPORT_PAUSE);
|
MediaPlayerEntity.SUPPORT_PAUSE);
|
||||||
bool get supportSeek => ((attributes["supported_features"] &
|
bool get supportSeek => ((supportedFeatures &
|
||||||
MediaPlayerEntity.SUPPORT_SEEK) ==
|
MediaPlayerEntity.SUPPORT_SEEK) ==
|
||||||
MediaPlayerEntity.SUPPORT_SEEK);
|
MediaPlayerEntity.SUPPORT_SEEK);
|
||||||
bool get supportVolumeSet => ((attributes["supported_features"] &
|
bool get supportVolumeSet => ((supportedFeatures &
|
||||||
MediaPlayerEntity.SUPPORT_VOLUME_SET) ==
|
MediaPlayerEntity.SUPPORT_VOLUME_SET) ==
|
||||||
MediaPlayerEntity.SUPPORT_VOLUME_SET);
|
MediaPlayerEntity.SUPPORT_VOLUME_SET);
|
||||||
bool get supportVolumeMute => ((attributes["supported_features"] &
|
bool get supportVolumeMute => ((supportedFeatures &
|
||||||
MediaPlayerEntity.SUPPORT_VOLUME_MUTE) ==
|
MediaPlayerEntity.SUPPORT_VOLUME_MUTE) ==
|
||||||
MediaPlayerEntity.SUPPORT_VOLUME_MUTE);
|
MediaPlayerEntity.SUPPORT_VOLUME_MUTE);
|
||||||
bool get supportPreviousTrack => ((attributes["supported_features"] &
|
bool get supportPreviousTrack => ((supportedFeatures &
|
||||||
MediaPlayerEntity.SUPPORT_PREVIOUS_TRACK) ==
|
MediaPlayerEntity.SUPPORT_PREVIOUS_TRACK) ==
|
||||||
MediaPlayerEntity.SUPPORT_PREVIOUS_TRACK);
|
MediaPlayerEntity.SUPPORT_PREVIOUS_TRACK);
|
||||||
bool get supportNextTrack => ((attributes["supported_features"] &
|
bool get supportNextTrack => ((supportedFeatures &
|
||||||
MediaPlayerEntity.SUPPORT_NEXT_TRACK) ==
|
MediaPlayerEntity.SUPPORT_NEXT_TRACK) ==
|
||||||
MediaPlayerEntity.SUPPORT_NEXT_TRACK);
|
MediaPlayerEntity.SUPPORT_NEXT_TRACK);
|
||||||
|
|
||||||
bool get supportTurnOn => ((attributes["supported_features"] &
|
bool get supportTurnOn => ((supportedFeatures &
|
||||||
MediaPlayerEntity.SUPPORT_TURN_ON) ==
|
MediaPlayerEntity.SUPPORT_TURN_ON) ==
|
||||||
MediaPlayerEntity.SUPPORT_TURN_ON);
|
MediaPlayerEntity.SUPPORT_TURN_ON);
|
||||||
bool get supportTurnOff => ((attributes["supported_features"] &
|
bool get supportTurnOff => ((supportedFeatures &
|
||||||
MediaPlayerEntity.SUPPORT_TURN_OFF) ==
|
MediaPlayerEntity.SUPPORT_TURN_OFF) ==
|
||||||
MediaPlayerEntity.SUPPORT_TURN_OFF);
|
MediaPlayerEntity.SUPPORT_TURN_OFF);
|
||||||
bool get supportPlayMedia => ((attributes["supported_features"] &
|
bool get supportPlayMedia => ((supportedFeatures &
|
||||||
MediaPlayerEntity.SUPPORT_PLAY_MEDIA) ==
|
MediaPlayerEntity.SUPPORT_PLAY_MEDIA) ==
|
||||||
MediaPlayerEntity.SUPPORT_PLAY_MEDIA);
|
MediaPlayerEntity.SUPPORT_PLAY_MEDIA);
|
||||||
bool get supportVolumeStep => ((attributes["supported_features"] &
|
bool get supportVolumeStep => ((supportedFeatures &
|
||||||
MediaPlayerEntity.SUPPORT_VOLUME_STEP) ==
|
MediaPlayerEntity.SUPPORT_VOLUME_STEP) ==
|
||||||
MediaPlayerEntity.SUPPORT_VOLUME_STEP);
|
MediaPlayerEntity.SUPPORT_VOLUME_STEP);
|
||||||
bool get supportSelectSource => ((attributes["supported_features"] &
|
bool get supportSelectSource => ((supportedFeatures &
|
||||||
MediaPlayerEntity.SUPPORT_SELECT_SOURCE) ==
|
MediaPlayerEntity.SUPPORT_SELECT_SOURCE) ==
|
||||||
MediaPlayerEntity.SUPPORT_SELECT_SOURCE);
|
MediaPlayerEntity.SUPPORT_SELECT_SOURCE);
|
||||||
bool get supportStop => ((attributes["supported_features"] &
|
bool get supportStop => ((supportedFeatures &
|
||||||
MediaPlayerEntity.SUPPORT_STOP) ==
|
MediaPlayerEntity.SUPPORT_STOP) ==
|
||||||
MediaPlayerEntity.SUPPORT_STOP);
|
MediaPlayerEntity.SUPPORT_STOP);
|
||||||
bool get supportClearPlaylist => ((attributes["supported_features"] &
|
bool get supportClearPlaylist => ((supportedFeatures &
|
||||||
MediaPlayerEntity.SUPPORT_CLEAR_PLAYLIST) ==
|
MediaPlayerEntity.SUPPORT_CLEAR_PLAYLIST) ==
|
||||||
MediaPlayerEntity.SUPPORT_CLEAR_PLAYLIST);
|
MediaPlayerEntity.SUPPORT_CLEAR_PLAYLIST);
|
||||||
bool get supportPlay => ((attributes["supported_features"] &
|
bool get supportPlay => ((supportedFeatures &
|
||||||
MediaPlayerEntity.SUPPORT_PLAY) ==
|
MediaPlayerEntity.SUPPORT_PLAY) ==
|
||||||
MediaPlayerEntity.SUPPORT_PLAY);
|
MediaPlayerEntity.SUPPORT_PLAY);
|
||||||
bool get supportShuffleSet => ((attributes["supported_features"] &
|
bool get supportShuffleSet => ((supportedFeatures &
|
||||||
MediaPlayerEntity.SUPPORT_SHUFFLE_SET) ==
|
MediaPlayerEntity.SUPPORT_SHUFFLE_SET) ==
|
||||||
MediaPlayerEntity.SUPPORT_SHUFFLE_SET);
|
MediaPlayerEntity.SUPPORT_SHUFFLE_SET);
|
||||||
bool get supportSelectSoundMode => ((attributes["supported_features"] &
|
bool get supportSelectSoundMode => ((supportedFeatures &
|
||||||
MediaPlayerEntity.SUPPORT_SELECT_SOUND_MODE) ==
|
MediaPlayerEntity.SUPPORT_SELECT_SOUND_MODE) ==
|
||||||
MediaPlayerEntity.SUPPORT_SELECT_SOUND_MODE);
|
MediaPlayerEntity.SUPPORT_SELECT_SOUND_MODE);
|
||||||
|
|
||||||
|
@ -47,7 +47,10 @@ class EntityCollection {
|
|||||||
case 'lock': {
|
case 'lock': {
|
||||||
return LockEntity(rawEntityData);
|
return LockEntity(rawEntityData);
|
||||||
}
|
}
|
||||||
case "automation":
|
case "automation": {
|
||||||
|
return AutomationEntity(rawEntityData);
|
||||||
|
}
|
||||||
|
|
||||||
case "input_boolean":
|
case "input_boolean":
|
||||||
case "switch": {
|
case "switch": {
|
||||||
return SwitchEntity(rawEntityData);
|
return SwitchEntity(rawEntityData);
|
||||||
@ -83,6 +86,12 @@ class EntityCollection {
|
|||||||
case "fan": {
|
case "fan": {
|
||||||
return FanEntity(rawEntityData);
|
return FanEntity(rawEntityData);
|
||||||
}
|
}
|
||||||
|
case "camera": {
|
||||||
|
return CameraEntity(rawEntityData);
|
||||||
|
}
|
||||||
|
case "alarm_control_panel": {
|
||||||
|
return AlarmControlPanelEntity(rawEntityData);
|
||||||
|
}
|
||||||
default: {
|
default: {
|
||||||
return Entity(rawEntityData);
|
return Entity(rawEntityData);
|
||||||
}
|
}
|
||||||
|
@ -23,7 +23,22 @@ class BadgeWidget extends StatelessWidget {
|
|||||||
);
|
);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
case "sensor":
|
case "camera":
|
||||||
|
case "media_player":
|
||||||
|
case "binary_sensor":
|
||||||
|
{
|
||||||
|
badgeIcon = MaterialDesignIcons.createIconWidgetFromEntityData(
|
||||||
|
entityModel.entityWrapper, iconSize, Colors.black);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case "device_tracker":
|
||||||
|
{
|
||||||
|
badgeIcon = MaterialDesignIcons.createIconWidgetFromEntityData(
|
||||||
|
entityModel.entityWrapper, iconSize, Colors.black);
|
||||||
|
onBadgeTextValue = entityModel.entityWrapper.entity.state;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
default:
|
||||||
{
|
{
|
||||||
onBadgeTextValue = entityModel.entityWrapper.entity.unitOfMeasurement;
|
onBadgeTextValue = entityModel.entityWrapper.entity.unitOfMeasurement;
|
||||||
badgeIcon = Center(
|
badgeIcon = Center(
|
||||||
@ -37,18 +52,6 @@ class BadgeWidget extends StatelessWidget {
|
|||||||
);
|
);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
case "device_tracker":
|
|
||||||
{
|
|
||||||
badgeIcon = MaterialDesignIcons.createIconWidgetFromEntityData(
|
|
||||||
entityModel.entityWrapper, iconSize, Colors.black);
|
|
||||||
onBadgeTextValue = entityModel.entityWrapper.entity.state;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
default:
|
|
||||||
{
|
|
||||||
badgeIcon = MaterialDesignIcons.createIconWidgetFromEntityData(
|
|
||||||
entityModel.entityWrapper, iconSize, Colors.black);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
Widget onBadgeText;
|
Widget onBadgeText;
|
||||||
if (onBadgeTextValue == null || onBadgeTextValue.length == 0) {
|
if (onBadgeTextValue == null || onBadgeTextValue.length == 0) {
|
||||||
|
175
lib/entity_widgets/common/camera_stream_view.dart
Normal file
175
lib/entity_widgets/common/camera_stream_view.dart
Normal file
@ -0,0 +1,175 @@
|
|||||||
|
part of '../../main.dart';
|
||||||
|
|
||||||
|
class CameraStreamView extends StatefulWidget {
|
||||||
|
|
||||||
|
CameraStreamView({Key key}) : super(key: key);
|
||||||
|
|
||||||
|
@override
|
||||||
|
_CameraStreamViewState createState() => _CameraStreamViewState();
|
||||||
|
}
|
||||||
|
|
||||||
|
class _CameraStreamViewState extends State<CameraStreamView> {
|
||||||
|
|
||||||
|
@override
|
||||||
|
void initState() {
|
||||||
|
super.initState();
|
||||||
|
}
|
||||||
|
|
||||||
|
CameraEntity _entity;
|
||||||
|
|
||||||
|
http.Client client;
|
||||||
|
http.StreamedResponse response;
|
||||||
|
List<int> binaryImage = [];
|
||||||
|
bool timeToStop = false;
|
||||||
|
Completer streamCompleter;
|
||||||
|
bool started = false;
|
||||||
|
bool useSVG = false;
|
||||||
|
|
||||||
|
void _connect() async {
|
||||||
|
started = true;
|
||||||
|
timeToStop = false;
|
||||||
|
String streamUrl = '$homeAssistantWebHost/api/camera_proxy_stream/${_entity.entityId}?token=${_entity.attributes['access_token']}';
|
||||||
|
client = new http.Client(); // create a client to make api calls
|
||||||
|
http.Request request = new http.Request("GET", Uri.parse(streamUrl)); // create get request
|
||||||
|
Logger.d("[Sending] ==> $streamUrl");
|
||||||
|
response = await client.send(request);
|
||||||
|
Logger.d("[Received] <== ${response.headers}");
|
||||||
|
String frameBoundary = response.headers['content-type'].split('boundary=')[1];
|
||||||
|
final int frameBoundarySize = frameBoundary.length;
|
||||||
|
List<int> primaryBuffer=[];
|
||||||
|
int imageSizeStart = 59;
|
||||||
|
int imageSizeEnd = 0;
|
||||||
|
int imageStart = 0;
|
||||||
|
int imageSize = 0;
|
||||||
|
String strBuffer = "";
|
||||||
|
String contentType = "";
|
||||||
|
streamCompleter = Completer();
|
||||||
|
response.stream.transform(
|
||||||
|
StreamTransformer.fromHandlers(
|
||||||
|
handleData: (data, sink) {
|
||||||
|
primaryBuffer.addAll(data);
|
||||||
|
imageStart = 0;
|
||||||
|
imageSizeEnd = 0;
|
||||||
|
if (primaryBuffer.length >= imageSizeStart + 10) {
|
||||||
|
contentType = utf8.decode(
|
||||||
|
primaryBuffer.sublist(frameBoundarySize+16, imageSizeStart + 10), allowMalformed: true).split("\r\n")[0];
|
||||||
|
useSVG = contentType == "image/svg+xml";
|
||||||
|
imageSizeStart = frameBoundarySize + 16 + contentType.length + 18;
|
||||||
|
for (int i = imageSizeStart; i < primaryBuffer.length - 4; i++) {
|
||||||
|
strBuffer = utf8.decode(
|
||||||
|
primaryBuffer.sublist(i, i + 4), allowMalformed: true);
|
||||||
|
if (strBuffer == "\r\n\r\n") {
|
||||||
|
imageSizeEnd = i;
|
||||||
|
imageStart = i + 4;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (imageSizeEnd > 0) {
|
||||||
|
imageSize = int.tryParse(utf8.decode(
|
||||||
|
primaryBuffer.sublist(imageSizeStart, imageSizeEnd),
|
||||||
|
allowMalformed: true));
|
||||||
|
//Logger.d("content-length: $imageSize");
|
||||||
|
if (imageSize != null &&
|
||||||
|
primaryBuffer.length >= imageStart + imageSize + 2) {
|
||||||
|
sink.add(
|
||||||
|
primaryBuffer.sublist(
|
||||||
|
imageStart, imageStart + imageSize));
|
||||||
|
primaryBuffer.removeRange(0, imageStart + imageSize + 2);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (timeToStop) {
|
||||||
|
sink?.close();
|
||||||
|
streamCompleter.complete();
|
||||||
|
}
|
||||||
|
},
|
||||||
|
handleError: (error, stack, sink) {
|
||||||
|
Logger.e("Error parsing MJPEG stream: $error");
|
||||||
|
},
|
||||||
|
handleDone: (sink) {
|
||||||
|
Logger.d("Camera stream finished. Reconnecting...");
|
||||||
|
sink?.close();
|
||||||
|
streamCompleter?.complete();
|
||||||
|
_reconnect();
|
||||||
|
},
|
||||||
|
)
|
||||||
|
).listen((d) {
|
||||||
|
if (!timeToStop) {
|
||||||
|
setState(() {
|
||||||
|
binaryImage = d;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
void _reconnect() {
|
||||||
|
disconnect().then((_){
|
||||||
|
_connect();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
Future disconnect() {
|
||||||
|
Completer disconF = Completer();
|
||||||
|
timeToStop = true;
|
||||||
|
if (streamCompleter != null && !streamCompleter.isCompleted) {
|
||||||
|
streamCompleter.future.then((_) {
|
||||||
|
client?.close();
|
||||||
|
disconF.complete();
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
client?.close();
|
||||||
|
disconF.complete();
|
||||||
|
}
|
||||||
|
return disconF.future;
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
Widget build(BuildContext context) {
|
||||||
|
if (!started) {
|
||||||
|
_entity = EntityModel
|
||||||
|
.of(context)
|
||||||
|
.entityWrapper
|
||||||
|
.entity;
|
||||||
|
_connect();
|
||||||
|
}
|
||||||
|
|
||||||
|
if (binaryImage.isEmpty) {
|
||||||
|
return Column(
|
||||||
|
children: <Widget>[
|
||||||
|
Container(
|
||||||
|
padding: const EdgeInsets.all(20.0),
|
||||||
|
child: const CircularProgressIndicator()
|
||||||
|
)
|
||||||
|
],
|
||||||
|
);
|
||||||
|
} else {
|
||||||
|
if (useSVG) {
|
||||||
|
return Column(
|
||||||
|
children: <Widget>[
|
||||||
|
SvgPicture.memory(
|
||||||
|
Uint8List.fromList(binaryImage),
|
||||||
|
placeholderBuilder: (BuildContext context) =>
|
||||||
|
new Container(
|
||||||
|
padding: const EdgeInsets.all(20.0),
|
||||||
|
child: const CircularProgressIndicator()
|
||||||
|
),
|
||||||
|
)
|
||||||
|
],
|
||||||
|
);
|
||||||
|
} else {
|
||||||
|
return Column(
|
||||||
|
children: <Widget>[
|
||||||
|
Image.memory(
|
||||||
|
Uint8List.fromList(binaryImage), gaplessPlayback: true),
|
||||||
|
],
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
void dispose() {
|
||||||
|
disconnect();
|
||||||
|
super.dispose();
|
||||||
|
}
|
||||||
|
}
|
@ -21,10 +21,13 @@ class EntityAttributesList extends StatelessWidget {
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
return Column(
|
return Padding(
|
||||||
|
padding: EdgeInsets.only(bottom: Sizes.rowPadding),
|
||||||
|
child: Column(
|
||||||
children: attrs,
|
children: attrs,
|
||||||
crossAxisAlignment: CrossAxisAlignment.start,
|
crossAxisAlignment: CrossAxisAlignment.start,
|
||||||
mainAxisSize: MainAxisSize.min,
|
mainAxisSize: MainAxisSize.min,
|
||||||
|
)
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
41
lib/entity_widgets/common/flat_service_button.dart
Normal file
41
lib/entity_widgets/common/flat_service_button.dart
Normal file
@ -0,0 +1,41 @@
|
|||||||
|
part of '../../main.dart';
|
||||||
|
|
||||||
|
class FlatServiceButton extends StatelessWidget {
|
||||||
|
|
||||||
|
final String serviceDomain;
|
||||||
|
final String serviceName;
|
||||||
|
final String entityId;
|
||||||
|
final String text;
|
||||||
|
final double fontSize;
|
||||||
|
|
||||||
|
FlatServiceButton({
|
||||||
|
Key key,
|
||||||
|
@required this.serviceDomain,
|
||||||
|
@required this.serviceName,
|
||||||
|
@required this.entityId,
|
||||||
|
@required this.text,
|
||||||
|
this.fontSize: Sizes.stateFontSize
|
||||||
|
}) : super(key: key);
|
||||||
|
|
||||||
|
void _setNewState() {
|
||||||
|
eventBus.fire(new ServiceCallEvent(serviceDomain, serviceName, entityId, null));
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
Widget build(BuildContext context) {
|
||||||
|
return SizedBox(
|
||||||
|
height: fontSize*2.5,
|
||||||
|
child: FlatButton(
|
||||||
|
onPressed: (() {
|
||||||
|
_setNewState();
|
||||||
|
}),
|
||||||
|
child: Text(
|
||||||
|
text,
|
||||||
|
textAlign: TextAlign.right,
|
||||||
|
style:
|
||||||
|
new TextStyle(fontSize: fontSize, color: Colors.blue),
|
||||||
|
),
|
||||||
|
)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
@ -6,7 +6,7 @@ class LastUpdatedWidget extends StatelessWidget {
|
|||||||
final entityModel = EntityModel.of(context);
|
final entityModel = EntityModel.of(context);
|
||||||
return Padding(
|
return Padding(
|
||||||
padding: EdgeInsets.fromLTRB(
|
padding: EdgeInsets.fromLTRB(
|
||||||
Sizes.leftWidgetPadding, 0.0, 0.0, 0.0),
|
Sizes.leftWidgetPadding, Sizes.rowPadding, 0.0, 0.0),
|
||||||
child: Text(
|
child: Text(
|
||||||
'${entityModel.entityWrapper.entity.lastUpdated}',
|
'${entityModel.entityWrapper.entity.lastUpdated}',
|
||||||
textAlign: TextAlign.left,
|
textAlign: TextAlign.left,
|
||||||
|
101
lib/entity_widgets/common/light_color_picker.dart
Normal file
101
lib/entity_widgets/common/light_color_picker.dart
Normal file
@ -0,0 +1,101 @@
|
|||||||
|
part of '../../main.dart';
|
||||||
|
|
||||||
|
class LightColorPicker extends StatefulWidget {
|
||||||
|
|
||||||
|
final HSVColor color;
|
||||||
|
final onColorSelected;
|
||||||
|
final double hueStep;
|
||||||
|
final double saturationStep;
|
||||||
|
final EdgeInsets padding;
|
||||||
|
|
||||||
|
LightColorPicker({this.color, this.onColorSelected, this.hueStep: 15.0, this.saturationStep: 0.2, this.padding: const EdgeInsets.fromLTRB(Sizes.leftWidgetPadding, Sizes.rowPadding, Sizes.rightWidgetPadding, 0.0)});
|
||||||
|
|
||||||
|
@override
|
||||||
|
LightColorPickerState createState() => new LightColorPickerState();
|
||||||
|
}
|
||||||
|
|
||||||
|
class LightColorPickerState extends State<LightColorPicker> {
|
||||||
|
|
||||||
|
@override
|
||||||
|
void initState() {
|
||||||
|
super.initState();
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
Widget build(BuildContext context) {
|
||||||
|
List<Widget> colorRows = [];
|
||||||
|
Border border;
|
||||||
|
bool isSomethingSelected = false;
|
||||||
|
Logger.d("Current colotfor picker: [${widget.color.hue}, ${widget.color.saturation}]");
|
||||||
|
for (double saturation = 1.0; saturation >= (0.0 + widget.saturationStep); saturation = double.parse((saturation - widget.saturationStep).toStringAsFixed(2))) {
|
||||||
|
List<Widget> rowChildren = [];
|
||||||
|
//Logger.d("$saturation");
|
||||||
|
double roundedSaturation = double.parse(widget.color.saturation.toStringAsFixed(1));
|
||||||
|
//Logger.d("Rounded saturation=$roundedSaturation");
|
||||||
|
for (double hue = 0; hue <= (365 - widget.hueStep);
|
||||||
|
hue += widget.hueStep) {
|
||||||
|
bool isExactHue = widget.color.hue.round() == hue;
|
||||||
|
bool isHueInRange = widget.color.hue.round() > hue && widget.color.hue.round() < (hue+widget.hueStep);
|
||||||
|
bool isExactSaturation = roundedSaturation == saturation;
|
||||||
|
bool isSaturationInRange = roundedSaturation > saturation && roundedSaturation < double.parse((saturation+widget.saturationStep).toStringAsFixed(1));
|
||||||
|
if ((isExactHue || isHueInRange) && (isExactSaturation || isSaturationInRange)) {
|
||||||
|
//Logger.d("$isExactHue $isHueInRange $isExactSaturation $isSaturationInRange (${saturation+widget.saturationStep})");
|
||||||
|
border = Border.all(
|
||||||
|
width: 2.0,
|
||||||
|
color: Colors.white,
|
||||||
|
);
|
||||||
|
isSomethingSelected = true;
|
||||||
|
} else {
|
||||||
|
border = null;
|
||||||
|
}
|
||||||
|
HSVColor currentColor = HSVColor.fromAHSV(1.0, hue, double.parse(saturation.toStringAsFixed(2)), 1.0);
|
||||||
|
rowChildren.add(
|
||||||
|
Flexible(
|
||||||
|
child: GestureDetector(
|
||||||
|
child: Container(
|
||||||
|
height: 40.0,
|
||||||
|
decoration: BoxDecoration(
|
||||||
|
color: currentColor.toColor(),
|
||||||
|
border: border,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
onTap: () => widget.onColorSelected(currentColor),
|
||||||
|
)
|
||||||
|
)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
colorRows.add(
|
||||||
|
Row(
|
||||||
|
children: rowChildren,
|
||||||
|
)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
colorRows.add(
|
||||||
|
Flexible(
|
||||||
|
child: GestureDetector(
|
||||||
|
child: Container(
|
||||||
|
height: 40.0,
|
||||||
|
decoration: BoxDecoration(
|
||||||
|
color: Colors.white,
|
||||||
|
border: isSomethingSelected ? null : Border.all(
|
||||||
|
width: 2.0,
|
||||||
|
color: Colors.amber[200],
|
||||||
|
)
|
||||||
|
),
|
||||||
|
),
|
||||||
|
onTap: () => widget.onColorSelected(HSVColor.fromAHSV(1.0, 30.0, 0.0, 1.0)),
|
||||||
|
)
|
||||||
|
)
|
||||||
|
);
|
||||||
|
return Padding(
|
||||||
|
child: Column(
|
||||||
|
mainAxisSize: MainAxisSize.min,
|
||||||
|
crossAxisAlignment: CrossAxisAlignment.center,
|
||||||
|
children: colorRows,
|
||||||
|
),
|
||||||
|
padding: widget.padding,
|
||||||
|
);
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
@ -7,8 +7,8 @@ class ModeSelectorWidget extends StatelessWidget {
|
|||||||
final String value;
|
final String value;
|
||||||
final double captionFontSize;
|
final double captionFontSize;
|
||||||
final double valueFontSize;
|
final double valueFontSize;
|
||||||
final double bottomPadding;
|
|
||||||
final onChange;
|
final onChange;
|
||||||
|
final EdgeInsets padding;
|
||||||
|
|
||||||
ModeSelectorWidget({
|
ModeSelectorWidget({
|
||||||
Key key,
|
Key key,
|
||||||
@ -18,12 +18,14 @@ class ModeSelectorWidget extends StatelessWidget {
|
|||||||
@required this.onChange,
|
@required this.onChange,
|
||||||
this.captionFontSize,
|
this.captionFontSize,
|
||||||
this.valueFontSize,
|
this.valueFontSize,
|
||||||
this.bottomPadding
|
this.padding: const EdgeInsets.fromLTRB(Sizes.leftWidgetPadding, Sizes.rowPadding, Sizes.rightWidgetPadding, 0.0),
|
||||||
}) : super(key: key);
|
}) : super(key: key);
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
return Column(
|
return Padding(
|
||||||
|
padding: padding,
|
||||||
|
child: Column(
|
||||||
crossAxisAlignment: CrossAxisAlignment.start,
|
crossAxisAlignment: CrossAxisAlignment.start,
|
||||||
children: <Widget>[
|
children: <Widget>[
|
||||||
Text("$caption", style: TextStyle(
|
Text("$caption", style: TextStyle(
|
||||||
@ -54,9 +56,9 @@ class ModeSelectorWidget extends StatelessWidget {
|
|||||||
),
|
),
|
||||||
)
|
)
|
||||||
],
|
],
|
||||||
),
|
)
|
||||||
Container(height: bottomPadding ?? Sizes.rowPadding,)
|
|
||||||
],
|
],
|
||||||
|
),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
@ -7,6 +7,7 @@ class ModeSwitchWidget extends StatelessWidget {
|
|||||||
final double captionFontSize;
|
final double captionFontSize;
|
||||||
final bool value;
|
final bool value;
|
||||||
final bool expanded;
|
final bool expanded;
|
||||||
|
final EdgeInsets padding;
|
||||||
|
|
||||||
ModeSwitchWidget({
|
ModeSwitchWidget({
|
||||||
Key key,
|
Key key,
|
||||||
@ -14,12 +15,15 @@ class ModeSwitchWidget extends StatelessWidget {
|
|||||||
@required this.onChange,
|
@required this.onChange,
|
||||||
this.captionFontSize,
|
this.captionFontSize,
|
||||||
this.value,
|
this.value,
|
||||||
this.expanded: true
|
this.expanded: true,
|
||||||
|
this.padding: const EdgeInsets.only(left: Sizes.leftWidgetPadding, right: Sizes.rightWidgetPadding)
|
||||||
}) : super(key: key);
|
}) : super(key: key);
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
return Row(
|
return Padding(
|
||||||
|
padding: this.padding,
|
||||||
|
child: Row(
|
||||||
children: <Widget>[
|
children: <Widget>[
|
||||||
_buildCaption(),
|
_buildCaption(),
|
||||||
Switch(
|
Switch(
|
||||||
@ -27,6 +31,7 @@ class ModeSwitchWidget extends StatelessWidget {
|
|||||||
value: value ?? false,
|
value: value ?? false,
|
||||||
)
|
)
|
||||||
],
|
],
|
||||||
|
)
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -10,8 +10,9 @@ class UniversalSlider extends StatelessWidget {
|
|||||||
final double min;
|
final double min;
|
||||||
final double max;
|
final double max;
|
||||||
final double value;
|
final double value;
|
||||||
|
final EdgeInsets padding;
|
||||||
|
|
||||||
const UniversalSlider({Key key, this.onChanged, this.onChangeEnd, this.leading, this.closing, this.title, this.min, this.max, this.value}) : super(key: key);
|
const UniversalSlider({Key key, this.onChanged, this.onChangeEnd, this.leading, this.closing, this.title, this.min, this.max, this.value, this.padding: const EdgeInsets.fromLTRB(Sizes.leftWidgetPadding, Sizes.rowPadding, Sizes.rightWidgetPadding, 0.0)}) : super(key: key);
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
@ -33,7 +34,9 @@ class UniversalSlider extends StatelessWidget {
|
|||||||
if (closing != null) {
|
if (closing != null) {
|
||||||
row.add(closing);
|
row.add(closing);
|
||||||
}
|
}
|
||||||
return Column(
|
return Padding(
|
||||||
|
padding: padding,
|
||||||
|
child: Column(
|
||||||
crossAxisAlignment: CrossAxisAlignment.start,
|
crossAxisAlignment: CrossAxisAlignment.start,
|
||||||
children: <Widget>[
|
children: <Widget>[
|
||||||
Container(height: Sizes.rowPadding,),
|
Container(height: Sizes.rowPadding,),
|
||||||
@ -48,6 +51,7 @@ class UniversalSlider extends StatelessWidget {
|
|||||||
),
|
),
|
||||||
Container(height: Sizes.rowPadding,)
|
Container(height: Sizes.rowPadding,)
|
||||||
],
|
],
|
||||||
|
),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
262
lib/entity_widgets/controls/alarm_control_panel_controls.dart
Normal file
262
lib/entity_widgets/controls/alarm_control_panel_controls.dart
Normal file
@ -0,0 +1,262 @@
|
|||||||
|
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) {
|
||||||
|
eventBus.fire(new ServiceCallEvent(
|
||||||
|
entity.domain, service, entity.entityId,
|
||||||
|
{"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: () {
|
||||||
|
eventBus.fire(new ServiceCallEvent(entity.domain, "alarm_trigger", entity.entityId, null));
|
||||||
|
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: TextStyle(color: Colors.redAccent)
|
||||||
|
),
|
||||||
|
onPressed: () => _askToTrigger(entity),
|
||||||
|
)
|
||||||
|
]
|
||||||
|
);
|
||||||
|
return Column(
|
||||||
|
crossAxisAlignment: CrossAxisAlignment.center,
|
||||||
|
children: <Widget>[
|
||||||
|
widget.extended ? buttonsWrapper : inputWrapper,
|
||||||
|
widget.extended ? inputWrapper : buttonsWrapper,
|
||||||
|
widget.extended ? pinPad : triggerButton
|
||||||
|
]
|
||||||
|
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
}
|
@ -13,6 +13,8 @@ class _ClimateControlWidgetState extends State<ClimateControlWidget> {
|
|||||||
bool _showPending = false;
|
bool _showPending = false;
|
||||||
bool _changedHere = false;
|
bool _changedHere = false;
|
||||||
Timer _resetTimer;
|
Timer _resetTimer;
|
||||||
|
Timer _tempThrottleTimer;
|
||||||
|
Timer _targetTempThrottleTimer;
|
||||||
double _tmpTemperature = 0.0;
|
double _tmpTemperature = 0.0;
|
||||||
double _tmpTargetLow = 0.0;
|
double _tmpTargetLow = 0.0;
|
||||||
double _tmpTargetHigh = 0.0;
|
double _tmpTargetHigh = 0.0;
|
||||||
@ -40,53 +42,69 @@ class _ClimateControlWidgetState extends State<ClimateControlWidget> {
|
|||||||
_changedHere = false;
|
_changedHere = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
void _temperatureUp(ClimateEntity entity, double step) {
|
void _temperatureUp(ClimateEntity entity) {
|
||||||
_tmpTemperature = ((_tmpTemperature + step) <= entity.maxTemp) ? _tmpTemperature + step : entity.maxTemp;
|
_tmpTemperature = ((_tmpTemperature + entity.temperatureStep) <= entity.maxTemp) ? _tmpTemperature + entity.temperatureStep : entity.maxTemp;
|
||||||
_setTemperature(entity);
|
_setTemperature(entity);
|
||||||
}
|
}
|
||||||
|
|
||||||
void _temperatureDown(ClimateEntity entity, double step) {
|
void _temperatureDown(ClimateEntity entity) {
|
||||||
_tmpTemperature = ((_tmpTemperature - step) >= entity.minTemp) ? _tmpTemperature - step : entity.minTemp;
|
_tmpTemperature = ((_tmpTemperature - entity.temperatureStep) >= entity.minTemp) ? _tmpTemperature - entity.temperatureStep : entity.minTemp;
|
||||||
_setTemperature(entity);
|
_setTemperature(entity);
|
||||||
}
|
}
|
||||||
|
|
||||||
void _targetLowUp(ClimateEntity entity, double step) {
|
void _targetLowUp(ClimateEntity entity) {
|
||||||
_tmpTargetLow = ((_tmpTargetLow + step) <= entity.maxTemp) ? _tmpTargetLow + step : entity.maxTemp;
|
_tmpTargetLow = ((_tmpTargetLow + entity.temperatureStep) <= entity.maxTemp) ? _tmpTargetLow + entity.temperatureStep : entity.maxTemp;
|
||||||
_setTargetTemp(entity);
|
_setTargetTemp(entity);
|
||||||
}
|
}
|
||||||
|
|
||||||
void _targetLowDown(ClimateEntity entity, double step) {
|
void _targetLowDown(ClimateEntity entity) {
|
||||||
_tmpTargetLow = ((_tmpTargetLow - step) >= entity.minTemp) ? _tmpTargetLow - step : entity.minTemp;
|
_tmpTargetLow = ((_tmpTargetLow - entity.temperatureStep) >= entity.minTemp) ? _tmpTargetLow - entity.temperatureStep : entity.minTemp;
|
||||||
_setTargetTemp(entity);
|
_setTargetTemp(entity);
|
||||||
}
|
}
|
||||||
|
|
||||||
void _targetHighUp(ClimateEntity entity, double step) {
|
void _targetHighUp(ClimateEntity entity) {
|
||||||
_tmpTargetHigh = ((_tmpTargetHigh + step) <= entity.maxTemp) ? _tmpTargetHigh + step : entity.maxTemp;
|
_tmpTargetHigh = ((_tmpTargetHigh + entity.temperatureStep) <= entity.maxTemp) ? _tmpTargetHigh + entity.temperatureStep : entity.maxTemp;
|
||||||
_setTargetTemp(entity);
|
_setTargetTemp(entity);
|
||||||
}
|
}
|
||||||
|
|
||||||
void _targetHighDown(ClimateEntity entity, double step) {
|
void _targetHighDown(ClimateEntity entity) {
|
||||||
_tmpTargetHigh = ((_tmpTargetHigh - step) >= entity.minTemp) ? _tmpTargetHigh - step : entity.minTemp;
|
_tmpTargetHigh = ((_tmpTargetHigh - entity.temperatureStep) >= entity.minTemp) ? _tmpTargetHigh - entity.temperatureStep : entity.minTemp;
|
||||||
_setTargetTemp(entity);
|
_setTargetTemp(entity);
|
||||||
}
|
}
|
||||||
|
|
||||||
void _setTemperature(ClimateEntity entity) {
|
void _setTemperature(ClimateEntity entity) {
|
||||||
|
if (_tempThrottleTimer!=null) {
|
||||||
|
_tempThrottleTimer.cancel();
|
||||||
|
}
|
||||||
setState(() {
|
setState(() {
|
||||||
|
_changedHere = true;
|
||||||
_tmpTemperature = double.parse(_tmpTemperature.toStringAsFixed(1));
|
_tmpTemperature = double.parse(_tmpTemperature.toStringAsFixed(1));
|
||||||
|
});
|
||||||
|
_tempThrottleTimer = Timer(Duration(seconds: 2), () {
|
||||||
|
setState(() {
|
||||||
_changedHere = true;
|
_changedHere = true;
|
||||||
eventBus.fire(new ServiceCallEvent(entity.domain, "set_temperature", entity.entityId,{"temperature": "${_tmpTemperature.toStringAsFixed(1)}"}));
|
eventBus.fire(new ServiceCallEvent(entity.domain, "set_temperature", entity.entityId,{"temperature": "${_tmpTemperature.toStringAsFixed(1)}"}));
|
||||||
_resetStateTimer(entity);
|
_resetStateTimer(entity);
|
||||||
});
|
});
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
void _setTargetTemp(ClimateEntity entity) {
|
void _setTargetTemp(ClimateEntity entity) {
|
||||||
|
if (_targetTempThrottleTimer!=null) {
|
||||||
|
_targetTempThrottleTimer.cancel();
|
||||||
|
}
|
||||||
setState(() {
|
setState(() {
|
||||||
|
_changedHere = true;
|
||||||
_tmpTargetLow = double.parse(_tmpTargetLow.toStringAsFixed(1));
|
_tmpTargetLow = double.parse(_tmpTargetLow.toStringAsFixed(1));
|
||||||
_tmpTargetHigh = double.parse(_tmpTargetHigh.toStringAsFixed(1));
|
_tmpTargetHigh = double.parse(_tmpTargetHigh.toStringAsFixed(1));
|
||||||
|
});
|
||||||
|
_targetTempThrottleTimer = Timer(Duration(seconds: 2), () {
|
||||||
|
setState(() {
|
||||||
_changedHere = true;
|
_changedHere = true;
|
||||||
eventBus.fire(new ServiceCallEvent(entity.domain, "set_temperature", entity.entityId,{"target_temp_high": "${_tmpTargetHigh.toStringAsFixed(1)}", "target_temp_low": "${_tmpTargetLow.toStringAsFixed(1)}"}));
|
eventBus.fire(new ServiceCallEvent(entity.domain, "set_temperature", entity.entityId,{"target_temp_high": "${_tmpTargetHigh.toStringAsFixed(1)}", "target_temp_low": "${_tmpTargetLow.toStringAsFixed(1)}"}));
|
||||||
_resetStateTimer(entity);
|
_resetStateTimer(entity);
|
||||||
});
|
});
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
void _setTargetHumidity(ClimateEntity entity, double value) {
|
void _setTargetHumidity(ClimateEntity entity, double value) {
|
||||||
@ -167,7 +185,7 @@ class _ClimateControlWidgetState extends State<ClimateControlWidget> {
|
|||||||
final entityModel = EntityModel.of(context);
|
final entityModel = EntityModel.of(context);
|
||||||
final ClimateEntity entity = entityModel.entityWrapper.entity;
|
final ClimateEntity entity = entityModel.entityWrapper.entity;
|
||||||
if (_changedHere) {
|
if (_changedHere) {
|
||||||
_showPending = (_tmpTemperature != entity.temperature);
|
_showPending = (_tmpTemperature != entity.temperature || _tmpTargetHigh != entity.targetHigh || _tmpTargetLow != entity.targetLow);
|
||||||
_changedHere = false;
|
_changedHere = false;
|
||||||
} else {
|
} else {
|
||||||
_resetTimer?.cancel();
|
_resetTimer?.cancel();
|
||||||
@ -278,10 +296,8 @@ class _ClimateControlWidgetState extends State<ClimateControlWidget> {
|
|||||||
TemperatureControlWidget(
|
TemperatureControlWidget(
|
||||||
value: _tmpTemperature,
|
value: _tmpTemperature,
|
||||||
fontColor: _showPending ? Colors.red : Colors.black,
|
fontColor: _showPending ? Colors.red : Colors.black,
|
||||||
onLargeDec: () => _temperatureDown(entity, 0.5),
|
onDec: () => _temperatureDown(entity),
|
||||||
onLargeInc: () => _temperatureUp(entity, 0.5),
|
onInc: () => _temperatureUp(entity),
|
||||||
onSmallDec: () => _temperatureDown(entity, 0.1),
|
|
||||||
onSmallInc: () => _temperatureUp(entity, 0.1),
|
|
||||||
)
|
)
|
||||||
],
|
],
|
||||||
);
|
);
|
||||||
@ -297,10 +313,8 @@ class _ClimateControlWidgetState extends State<ClimateControlWidget> {
|
|||||||
TemperatureControlWidget(
|
TemperatureControlWidget(
|
||||||
value: _tmpTargetLow,
|
value: _tmpTargetLow,
|
||||||
fontColor: _showPending ? Colors.red : Colors.black,
|
fontColor: _showPending ? Colors.red : Colors.black,
|
||||||
onLargeDec: () => _targetLowDown(entity, 0.5),
|
onDec: () => _targetLowDown(entity),
|
||||||
onLargeInc: () => _targetLowUp(entity, 0.5),
|
onInc: () => _targetLowUp(entity),
|
||||||
onSmallDec: () => _targetLowDown(entity, 0.1),
|
|
||||||
onSmallInc: () => _targetLowUp(entity, 0.1),
|
|
||||||
),
|
),
|
||||||
Expanded(
|
Expanded(
|
||||||
child: Container(height: 10.0),
|
child: Container(height: 10.0),
|
||||||
@ -312,10 +326,8 @@ class _ClimateControlWidgetState extends State<ClimateControlWidget> {
|
|||||||
TemperatureControlWidget(
|
TemperatureControlWidget(
|
||||||
value: _tmpTargetHigh,
|
value: _tmpTargetHigh,
|
||||||
fontColor: _showPending ? Colors.red : Colors.black,
|
fontColor: _showPending ? Colors.red : Colors.black,
|
||||||
onLargeDec: () => _targetHighDown(entity, 0.5),
|
onDec: () => _targetHighDown(entity),
|
||||||
onLargeInc: () => _targetHighUp(entity, 0.5),
|
onInc: () => _targetHighUp(entity),
|
||||||
onSmallDec: () => _targetHighDown(entity, 0.1),
|
|
||||||
onSmallInc: () => _targetHighUp(entity, 0.1),
|
|
||||||
)
|
)
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
@ -401,18 +413,14 @@ class TemperatureControlWidget extends StatelessWidget {
|
|||||||
final double value;
|
final double value;
|
||||||
final double fontSize;
|
final double fontSize;
|
||||||
final Color fontColor;
|
final Color fontColor;
|
||||||
final onSmallInc;
|
final onInc;
|
||||||
final onLargeInc;
|
final onDec;
|
||||||
final onSmallDec;
|
|
||||||
final onLargeDec;
|
|
||||||
|
|
||||||
TemperatureControlWidget(
|
TemperatureControlWidget(
|
||||||
{Key key,
|
{Key key,
|
||||||
@required this.value,
|
@required this.value,
|
||||||
@required this.onSmallInc,
|
@required this.onInc,
|
||||||
@required this.onSmallDec,
|
@required this.onDec,
|
||||||
@required this.onLargeInc,
|
|
||||||
@required this.onLargeDec,
|
|
||||||
this.fontSize,
|
this.fontSize,
|
||||||
this.fontColor})
|
this.fontColor})
|
||||||
: super(key: key);
|
: super(key: key);
|
||||||
@ -435,29 +443,13 @@ class TemperatureControlWidget extends StatelessWidget {
|
|||||||
icon: Icon(MaterialDesignIcons.createIconDataFromIconName(
|
icon: Icon(MaterialDesignIcons.createIconDataFromIconName(
|
||||||
'mdi:chevron-up')),
|
'mdi:chevron-up')),
|
||||||
iconSize: 30.0,
|
iconSize: 30.0,
|
||||||
onPressed: () => onSmallInc(),
|
onPressed: () => onInc(),
|
||||||
),
|
),
|
||||||
IconButton(
|
IconButton(
|
||||||
icon: Icon(MaterialDesignIcons.createIconDataFromIconName(
|
icon: Icon(MaterialDesignIcons.createIconDataFromIconName(
|
||||||
'mdi:chevron-down')),
|
'mdi:chevron-down')),
|
||||||
iconSize: 30.0,
|
iconSize: 30.0,
|
||||||
onPressed: () => onSmallDec(),
|
onPressed: () => onDec(),
|
||||||
)
|
|
||||||
],
|
|
||||||
),
|
|
||||||
Column(
|
|
||||||
children: <Widget>[
|
|
||||||
IconButton(
|
|
||||||
icon: Icon(MaterialDesignIcons.createIconDataFromIconName(
|
|
||||||
'mdi:chevron-double-up')),
|
|
||||||
iconSize: 30.0,
|
|
||||||
onPressed: () => onLargeInc(),
|
|
||||||
),
|
|
||||||
IconButton(
|
|
||||||
icon: Icon(MaterialDesignIcons.createIconDataFromIconName(
|
|
||||||
'mdi:chevron-double-down')),
|
|
||||||
iconSize: 30.0,
|
|
||||||
onPressed: () => onLargeDec(),
|
|
||||||
)
|
)
|
||||||
],
|
],
|
||||||
)
|
)
|
||||||
|
@ -10,16 +10,16 @@ class LightControlsWidget extends StatefulWidget {
|
|||||||
class _LightControlsWidgetState extends State<LightControlsWidget> {
|
class _LightControlsWidgetState extends State<LightControlsWidget> {
|
||||||
|
|
||||||
int _tmpBrightness;
|
int _tmpBrightness;
|
||||||
int _tmpColorTemp;
|
int _tmpColorTemp = 0;
|
||||||
Color _tmpColor;
|
HSVColor _tmpColor = HSVColor.fromAHSV(1.0, 30.0, 0.0, 1.0);
|
||||||
bool _changedHere = false;
|
bool _changedHere = false;
|
||||||
String _tmpEffect;
|
String _tmpEffect;
|
||||||
|
|
||||||
void _resetState(LightEntity entity) {
|
void _resetState(LightEntity entity) {
|
||||||
_tmpBrightness = entity.brightness ?? 0;
|
_tmpBrightness = entity.brightness ?? 0;
|
||||||
_tmpColorTemp = entity.colorTemp;
|
_tmpColorTemp = entity.colorTemp ?? entity.minMireds?.toInt();
|
||||||
_tmpColor = entity.color;
|
_tmpColor = entity.color ?? _tmpColor;
|
||||||
_tmpEffect = null;
|
_tmpEffect = entity.effect;
|
||||||
}
|
}
|
||||||
|
|
||||||
void _setBrightness(LightEntity entity, double value) {
|
void _setBrightness(LightEntity entity, double value) {
|
||||||
@ -48,20 +48,14 @@ class _LightControlsWidgetState extends State<LightControlsWidget> {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
void _setColor(LightEntity entity, Color color) {
|
void _setColor(LightEntity entity, HSVColor color) {
|
||||||
setState(() {
|
setState(() {
|
||||||
_tmpColor = color;
|
_tmpColor = color;
|
||||||
_changedHere = true;
|
_changedHere = true;
|
||||||
Logger.d( "Color: [${color.red}, ${color.green}, ${color.blue}]");
|
Logger.d( "HS Color: [${color.hue}, ${color.saturation}]");
|
||||||
if ((color == Colors.black) || ((color.red == color.green) && (color.green == color.blue))) {
|
|
||||||
eventBus.fire(new ServiceCallEvent(
|
|
||||||
entity.domain, "turn_off", entity.entityId,
|
|
||||||
null));
|
|
||||||
} else {
|
|
||||||
eventBus.fire(new ServiceCallEvent(
|
eventBus.fire(new ServiceCallEvent(
|
||||||
entity.domain, "turn_on", entity.entityId,
|
entity.domain, "turn_on", entity.entityId,
|
||||||
{"rgb_color": [color.red, color.green, color.blue]}));
|
{"hs_color": [color.hue, color.saturation*100]}));
|
||||||
}
|
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -98,7 +92,7 @@ class _LightControlsWidgetState extends State<LightControlsWidget> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
Widget _buildBrightnessControl(LightEntity entity) {
|
Widget _buildBrightnessControl(LightEntity entity) {
|
||||||
if ((entity.supportBrightness) && (_tmpBrightness != null) && (entity.state != EntityState.unavailable)) {
|
if ((entity.supportBrightness) && (_tmpBrightness != null)) {
|
||||||
return UniversalSlider(
|
return UniversalSlider(
|
||||||
onChanged: (value) {
|
onChanged: (value) {
|
||||||
setState(() {
|
setState(() {
|
||||||
@ -109,7 +103,7 @@ class _LightControlsWidgetState extends State<LightControlsWidget> {
|
|||||||
min: 0.0,
|
min: 0.0,
|
||||||
max: 255.0,
|
max: 255.0,
|
||||||
onChangeEnd: (value) => _setBrightness(entity, value),
|
onChangeEnd: (value) => _setBrightness(entity, value),
|
||||||
value: _tmpBrightness.toDouble(),
|
value: _tmpBrightness == null ? 0.0 : _tmpBrightness.toDouble(),
|
||||||
leading: Icon(Icons.brightness_5),
|
leading: Icon(Icons.brightness_5),
|
||||||
title: "Brightness",
|
title: "Brightness",
|
||||||
);
|
);
|
||||||
@ -119,11 +113,11 @@ class _LightControlsWidgetState extends State<LightControlsWidget> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
Widget _buildColorTempControl(LightEntity entity) {
|
Widget _buildColorTempControl(LightEntity entity) {
|
||||||
if ((entity.supportColorTemp) && (_tmpColorTemp != null)) {
|
if (entity.supportColorTemp) {
|
||||||
return UniversalSlider(
|
return UniversalSlider(
|
||||||
title: "Color temperature",
|
title: "Color temperature",
|
||||||
leading: Text("Cold", style: TextStyle(color: Colors.lightBlue),),
|
leading: Text("Cold", style: TextStyle(color: Colors.lightBlue),),
|
||||||
value: _tmpColorTemp.toDouble(),
|
value: _tmpColorTemp == null ? entity.maxMireds : _tmpColorTemp.toDouble(),
|
||||||
onChangeEnd: (value) => _setColorTemp(entity, value),
|
onChangeEnd: (value) => _setColorTemp(entity, value),
|
||||||
max: entity.maxMireds,
|
max: entity.maxMireds,
|
||||||
min: entity.minMireds,
|
min: entity.minMireds,
|
||||||
@ -141,26 +135,10 @@ class _LightControlsWidgetState extends State<LightControlsWidget> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
Widget _buildColorControl(LightEntity entity) {
|
Widget _buildColorControl(LightEntity entity) {
|
||||||
if ((entity.supportColor) && (entity.color != null)) {
|
if (entity.supportColor) {
|
||||||
return Column(
|
return LightColorPicker(
|
||||||
crossAxisAlignment: CrossAxisAlignment.center,
|
color: _tmpColor,
|
||||||
children: <Widget>[
|
onColorSelected: (color) => _setColor(entity, color),
|
||||||
Container(height: Sizes.rowPadding,),
|
|
||||||
RaisedButton(
|
|
||||||
onPressed: () => _showColorPicker(entity),
|
|
||||||
color: _tmpColor ?? Colors.black45,
|
|
||||||
child: Text(
|
|
||||||
"COLOR",
|
|
||||||
textAlign: TextAlign.center,
|
|
||||||
style: TextStyle(
|
|
||||||
fontSize: 50.0,
|
|
||||||
fontWeight: FontWeight.bold,
|
|
||||||
color: Colors.black12,
|
|
||||||
),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
Container(height: 2*Sizes.rowPadding,),
|
|
||||||
],
|
|
||||||
);
|
);
|
||||||
} else {
|
} else {
|
||||||
return Container(width: 0.0, height: 0.0);
|
return Container(width: 0.0, height: 0.0);
|
||||||
@ -174,15 +152,8 @@ class _LightControlsWidgetState extends State<LightControlsWidget> {
|
|||||||
return AlertDialog(
|
return AlertDialog(
|
||||||
titlePadding: EdgeInsets.all(0.0),
|
titlePadding: EdgeInsets.all(0.0),
|
||||||
contentPadding: EdgeInsets.all(0.0),
|
contentPadding: EdgeInsets.all(0.0),
|
||||||
content: SingleChildScrollView(
|
content: LightColorPicker(
|
||||||
child: MaterialPicker(
|
color: _tmpColor,
|
||||||
pickerColor: _tmpColor,
|
|
||||||
onColorChanged: (color) {
|
|
||||||
_setColor(entity, color);
|
|
||||||
Navigator.of(context).pop();
|
|
||||||
},
|
|
||||||
enableLabel: true,
|
|
||||||
),
|
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
},
|
},
|
||||||
|
@ -23,6 +23,15 @@ class EntityColor {
|
|||||||
"cool": Colors.lightBlue,
|
"cool": Colors.lightBlue,
|
||||||
EntityState.unavailable: Colors.black26,
|
EntityState.unavailable: Colors.black26,
|
||||||
EntityState.unknown: Colors.black26,
|
EntityState.unknown: Colors.black26,
|
||||||
|
EntityState.alarm_disarmed: Colors.green,
|
||||||
|
EntityState.alarm_armed_away: Colors.redAccent,
|
||||||
|
EntityState.alarm_armed_custom_bypass: Colors.redAccent,
|
||||||
|
EntityState.alarm_armed_home: Colors.redAccent,
|
||||||
|
EntityState.alarm_armed_night: Colors.redAccent,
|
||||||
|
EntityState.alarm_triggered: Colors.redAccent,
|
||||||
|
EntityState.alarm_arming: Colors.amber,
|
||||||
|
EntityState.alarm_disarming: Colors.amber,
|
||||||
|
EntityState.alarm_pending: Colors.amber,
|
||||||
};
|
};
|
||||||
|
|
||||||
static Color stateColor(String state) {
|
static Color stateColor(String state) {
|
||||||
|
@ -1,27 +0,0 @@
|
|||||||
part of '../../main.dart';
|
|
||||||
|
|
||||||
class ButtonStateWidget extends StatelessWidget {
|
|
||||||
|
|
||||||
void _setNewState(Entity entity) {
|
|
||||||
eventBus.fire(new ServiceCallEvent(entity.domain, "turn_on", entity.entityId, null));
|
|
||||||
}
|
|
||||||
|
|
||||||
@override
|
|
||||||
Widget build(BuildContext context) {
|
|
||||||
final entityModel = EntityModel.of(context);
|
|
||||||
return SizedBox(
|
|
||||||
height: 34.0,
|
|
||||||
child: FlatButton(
|
|
||||||
onPressed: (() {
|
|
||||||
_setNewState(entityModel.entityWrapper.entity);
|
|
||||||
}),
|
|
||||||
child: Text(
|
|
||||||
"EXECUTE",
|
|
||||||
textAlign: TextAlign.right,
|
|
||||||
style:
|
|
||||||
new TextStyle(fontSize: Sizes.stateFontSize, color: Colors.blue),
|
|
||||||
),
|
|
||||||
)
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
@ -3,7 +3,6 @@ part of 'main.dart';
|
|||||||
class HomeAssistant {
|
class HomeAssistant {
|
||||||
String _webSocketAPIEndpoint;
|
String _webSocketAPIEndpoint;
|
||||||
String _password;
|
String _password;
|
||||||
String _authType;
|
|
||||||
bool _useLovelace = false;
|
bool _useLovelace = false;
|
||||||
|
|
||||||
IOWebSocketChannel _hassioChannel;
|
IOWebSocketChannel _hassioChannel;
|
||||||
@ -56,10 +55,9 @@ class HomeAssistant {
|
|||||||
_messageQueue = SendMessageQueue(messageExpirationTime);
|
_messageQueue = SendMessageQueue(messageExpirationTime);
|
||||||
}
|
}
|
||||||
|
|
||||||
void updateSettings(String url, String password, String authType, bool useLovelace) {
|
void updateSettings(String url, String password, bool useLovelace) {
|
||||||
_webSocketAPIEndpoint = url;
|
_webSocketAPIEndpoint = url;
|
||||||
_password = password;
|
_password = password;
|
||||||
_authType = authType;
|
|
||||||
_useLovelace = useLovelace;
|
_useLovelace = useLovelace;
|
||||||
Logger.d( "Use lovelace is $_useLovelace");
|
Logger.d( "Use lovelace is $_useLovelace");
|
||||||
}
|
}
|
||||||
@ -213,7 +211,7 @@ class HomeAssistant {
|
|||||||
_handleMessage(String message) {
|
_handleMessage(String message) {
|
||||||
var data = json.decode(message);
|
var data = json.decode(message);
|
||||||
if (data["type"] == "auth_required") {
|
if (data["type"] == "auth_required") {
|
||||||
_sendAuthMessageRaw('{"type": "auth","$_authType": "$_password"}');
|
_sendAuthMessageRaw('{"type": "auth","access_token": "$_password"}');
|
||||||
} else if (data["type"] == "auth_ok") {
|
} else if (data["type"] == "auth_ok") {
|
||||||
_completeConnecting(null);
|
_completeConnecting(null);
|
||||||
_sendSubscribe();
|
_sendSubscribe();
|
||||||
@ -419,6 +417,16 @@ class HomeAssistant {
|
|||||||
name: rawView['title'],
|
name: rawView['title'],
|
||||||
iconName: rawView['icon']
|
iconName: rawView['icon']
|
||||||
);
|
);
|
||||||
|
|
||||||
|
if (rawView['badges'] != null && rawView['badges'] is List) {
|
||||||
|
rawView['badges'].forEach((entity) {
|
||||||
|
if (entities.isExist(entity)) {
|
||||||
|
Entity e = entities.get(entity);
|
||||||
|
view.badges.add(e);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
view.cards.addAll(_createLovelaceCards(rawView["cards"] ?? []));
|
view.cards.addAll(_createLovelaceCards(rawView["cards"] ?? []));
|
||||||
ui.views.add(
|
ui.views.add(
|
||||||
view
|
view
|
||||||
@ -430,16 +438,27 @@ class HomeAssistant {
|
|||||||
List<HACard> _createLovelaceCards(List rawCards) {
|
List<HACard> _createLovelaceCards(List rawCards) {
|
||||||
List<HACard> result = [];
|
List<HACard> result = [];
|
||||||
rawCards.forEach((rawCard){
|
rawCards.forEach((rawCard){
|
||||||
|
try {
|
||||||
bool isThereCardOptionsInside = rawCard["card"] != null;
|
bool isThereCardOptionsInside = rawCard["card"] != null;
|
||||||
HACard card = HACard(
|
HACard card = HACard(
|
||||||
id: "card",
|
id: "card",
|
||||||
name: isThereCardOptionsInside ? rawCard["card"]["title"] ?? rawCard["card"]["name"] : rawCard["title"] ?? rawCard["name"],
|
name: isThereCardOptionsInside ? rawCard["card"]["title"] ??
|
||||||
type: isThereCardOptionsInside ? rawCard["card"]['type'] : rawCard['type'],
|
rawCard["card"]["name"] : rawCard["title"] ?? rawCard["name"],
|
||||||
columnsCount: isThereCardOptionsInside ? rawCard["card"]['columns'] ?? 4 : rawCard['columns'] ?? 4,
|
type: isThereCardOptionsInside
|
||||||
showName: isThereCardOptionsInside ? rawCard["card"]['show_name'] ?? true : rawCard['show_name'] ?? true,
|
? rawCard["card"]['type']
|
||||||
showState: isThereCardOptionsInside ? rawCard["card"]['show_state'] ?? true : rawCard['show_state'] ?? true,
|
: rawCard['type'],
|
||||||
|
columnsCount: isThereCardOptionsInside
|
||||||
|
? rawCard["card"]['columns'] ?? 4
|
||||||
|
: rawCard['columns'] ?? 4,
|
||||||
|
showName: isThereCardOptionsInside ? rawCard["card"]['show_name'] ??
|
||||||
|
true : rawCard['show_name'] ?? true,
|
||||||
|
showState: isThereCardOptionsInside
|
||||||
|
? rawCard["card"]['show_state'] ?? true
|
||||||
|
: rawCard['show_state'] ?? true,
|
||||||
showEmpty: rawCard['show_empty'] ?? true,
|
showEmpty: rawCard['show_empty'] ?? true,
|
||||||
stateFilter: rawCard['state_filter'] ?? []
|
stateFilter: rawCard['state_filter'] ?? [],
|
||||||
|
states: rawCard['states'],
|
||||||
|
content: rawCard['content']
|
||||||
);
|
);
|
||||||
if (rawCard["cards"] != null) {
|
if (rawCard["cards"] != null) {
|
||||||
card.childCards = _createLovelaceCards(rawCard["cards"]);
|
card.childCards = _createLovelaceCards(rawCard["cards"]);
|
||||||
@ -470,6 +489,8 @@ class HomeAssistant {
|
|||||||
Entity e = entities.get(en);
|
Entity e = entities.get(en);
|
||||||
card.linkedEntityWrapper = EntityWrapper(
|
card.linkedEntityWrapper = EntityWrapper(
|
||||||
entity: e,
|
entity: e,
|
||||||
|
icon: rawCard["icon"],
|
||||||
|
displayName: rawCard["name"],
|
||||||
uiAction: EntityUIAction(rawEntityData: rawCard)
|
uiAction: EntityUIAction(rawEntityData: rawCard)
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
@ -484,9 +505,11 @@ class HomeAssistant {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
result.add(card);
|
result.add(card);
|
||||||
|
} catch (e) {
|
||||||
|
Logger.e("There was an error parsing card: ${e.toString()}");
|
||||||
|
}
|
||||||
});
|
});
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
@ -547,17 +570,10 @@ class HomeAssistant {
|
|||||||
String url = "$homeAssistantWebHost/api/history/period/$startTime?&filter_entity_id=$entityId";
|
String url = "$homeAssistantWebHost/api/history/period/$startTime?&filter_entity_id=$entityId";
|
||||||
Logger.d("[Sending] ==> $url");
|
Logger.d("[Sending] ==> $url");
|
||||||
http.Response historyResponse;
|
http.Response historyResponse;
|
||||||
if (_authType == "access_token") {
|
|
||||||
historyResponse = await http.get(url, headers: {
|
historyResponse = await http.get(url, headers: {
|
||||||
"authorization": "Bearer $_password",
|
"authorization": "Bearer $_password",
|
||||||
"Content-Type": "application/json"
|
"Content-Type": "application/json"
|
||||||
});
|
});
|
||||||
} else {
|
|
||||||
historyResponse = await http.get(url, headers: {
|
|
||||||
"X-HA-Access": "$_password",
|
|
||||||
"Content-Type": "application/json"
|
|
||||||
});
|
|
||||||
}
|
|
||||||
var history = json.decode(historyResponse.body);
|
var history = json.decode(historyResponse.body);
|
||||||
if (history is List) {
|
if (history is List) {
|
||||||
Logger.d( "[Received] <== ${history.first.length} history recors");
|
Logger.d( "[Received] <== ${history.first.length} history recors");
|
||||||
|
@ -1,5 +1,6 @@
|
|||||||
import 'dart:convert';
|
import 'dart:convert';
|
||||||
import 'dart:async';
|
import 'dart:async';
|
||||||
|
import 'dart:typed_data';
|
||||||
import 'package:flutter/rendering.dart';
|
import 'package:flutter/rendering.dart';
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'package:shared_preferences/shared_preferences.dart';
|
import 'package:shared_preferences/shared_preferences.dart';
|
||||||
@ -11,9 +12,10 @@ import 'package:url_launcher/url_launcher.dart';
|
|||||||
import 'package:flutter/services.dart';
|
import 'package:flutter/services.dart';
|
||||||
import 'package:date_format/date_format.dart';
|
import 'package:date_format/date_format.dart';
|
||||||
import 'package:http/http.dart' as http;
|
import 'package:http/http.dart' as http;
|
||||||
import 'package:flutter_colorpicker/material_picker.dart';
|
|
||||||
import 'package:charts_flutter/flutter.dart' as charts;
|
import 'package:charts_flutter/flutter.dart' as charts;
|
||||||
import 'package:progress_indicators/progress_indicators.dart';
|
import 'package:progress_indicators/progress_indicators.dart';
|
||||||
|
import 'package:flutter_markdown/flutter_markdown.dart';
|
||||||
|
import 'package:flutter_svg/flutter_svg.dart';
|
||||||
|
|
||||||
part 'entity_class/const.dart';
|
part 'entity_class/const.dart';
|
||||||
part 'entity_class/entity.class.dart';
|
part 'entity_class/entity.class.dart';
|
||||||
@ -32,6 +34,9 @@ part 'entity_class/media_player_entity.class.dart';
|
|||||||
part 'entity_class/lock_entity.class.dart';
|
part 'entity_class/lock_entity.class.dart';
|
||||||
part 'entity_class/group_entity.class.dart';
|
part 'entity_class/group_entity.class.dart';
|
||||||
part 'entity_class/fan_entity.class.dart';
|
part 'entity_class/fan_entity.class.dart';
|
||||||
|
part 'entity_class/automation_entity.dart';
|
||||||
|
part 'entity_class/camera_entity.class.dart';
|
||||||
|
part 'entity_class/alarm_control_panel.class.dart';
|
||||||
part 'entity_widgets/common/badge.dart';
|
part 'entity_widgets/common/badge.dart';
|
||||||
part 'entity_widgets/model_widgets.dart';
|
part 'entity_widgets/model_widgets.dart';
|
||||||
part 'entity_widgets/default_entity_container.dart';
|
part 'entity_widgets/default_entity_container.dart';
|
||||||
@ -44,6 +49,9 @@ part 'entity_widgets/common/last_updated.dart';
|
|||||||
part 'entity_widgets/common/mode_swicth.dart';
|
part 'entity_widgets/common/mode_swicth.dart';
|
||||||
part 'entity_widgets/common/mode_selector.dart';
|
part 'entity_widgets/common/mode_selector.dart';
|
||||||
part 'entity_widgets/common/universal_slider.dart';
|
part 'entity_widgets/common/universal_slider.dart';
|
||||||
|
part 'entity_widgets/common/flat_service_button.dart';
|
||||||
|
part 'entity_widgets/common/light_color_picker.dart';
|
||||||
|
part 'entity_widgets/common/camera_stream_view.dart';
|
||||||
part 'entity_widgets/entity_colors.class.dart';
|
part 'entity_widgets/entity_colors.class.dart';
|
||||||
part 'entity_widgets/entity_page_container.dart';
|
part 'entity_widgets/entity_page_container.dart';
|
||||||
part 'entity_widgets/history_chart/entity_history.dart';
|
part 'entity_widgets/history_chart/entity_history.dart';
|
||||||
@ -60,14 +68,15 @@ part 'entity_widgets/state/simple_state.dart';
|
|||||||
part 'entity_widgets/state/climate_state.dart';
|
part 'entity_widgets/state/climate_state.dart';
|
||||||
part 'entity_widgets/state/cover_state.dart';
|
part 'entity_widgets/state/cover_state.dart';
|
||||||
part 'entity_widgets/state/date_time_state.dart';
|
part 'entity_widgets/state/date_time_state.dart';
|
||||||
part 'entity_widgets/state/button_state.dart';
|
|
||||||
part 'entity_widgets/state/lock_state.dart';
|
part 'entity_widgets/state/lock_state.dart';
|
||||||
part 'entity_widgets/controls/climate_controls.dart';
|
part 'entity_widgets/controls/climate_controls.dart';
|
||||||
part 'entity_widgets/controls/cover_controls.dart';
|
part 'entity_widgets/controls/cover_controls.dart';
|
||||||
part 'entity_widgets/controls/light_controls.dart';
|
part 'entity_widgets/controls/light_controls.dart';
|
||||||
part 'entity_widgets/controls/media_player_widgets.dart';
|
part 'entity_widgets/controls/media_player_widgets.dart';
|
||||||
part 'entity_widgets/controls/fan_controls.dart';
|
part 'entity_widgets/controls/fan_controls.dart';
|
||||||
|
part 'entity_widgets/controls/alarm_control_panel_controls.dart';
|
||||||
part 'settings.page.dart';
|
part 'settings.page.dart';
|
||||||
|
part 'configuration.page.dart';
|
||||||
part 'home_assistant.class.dart';
|
part 'home_assistant.class.dart';
|
||||||
part 'log.page.dart';
|
part 'log.page.dart';
|
||||||
part 'entity.page.dart';
|
part 'entity.page.dart';
|
||||||
@ -85,7 +94,7 @@ part 'ui_widgets/card_header_widget.dart';
|
|||||||
|
|
||||||
EventBus eventBus = new EventBus();
|
EventBus eventBus = new EventBus();
|
||||||
const String appName = "HA Client";
|
const String appName = "HA Client";
|
||||||
const appVersion = "0.3.13";
|
const appVersion = "0.4.4";
|
||||||
|
|
||||||
String homeAssistantWebHost;
|
String homeAssistantWebHost;
|
||||||
|
|
||||||
@ -121,6 +130,7 @@ class HAClientApp extends StatelessWidget {
|
|||||||
routes: {
|
routes: {
|
||||||
"/": (context) => MainPage(title: 'HA Client'),
|
"/": (context) => MainPage(title: 'HA Client'),
|
||||||
"/connection-settings": (context) => ConnectionSettingsPage(title: "Settings"),
|
"/connection-settings": (context) => ConnectionSettingsPage(title: "Settings"),
|
||||||
|
"/configuration": (context) => ConfigurationPage(title: "Configuration"),
|
||||||
"/log-view": (context) => LogViewPage(title: "Log")
|
"/log-view": (context) => LogViewPage(title: "Log")
|
||||||
},
|
},
|
||||||
);
|
);
|
||||||
@ -141,14 +151,12 @@ class _MainPageState extends State<MainPage> with WidgetsBindingObserver {
|
|||||||
//Map _instanceConfig;
|
//Map _instanceConfig;
|
||||||
String _webSocketApiEndpoint;
|
String _webSocketApiEndpoint;
|
||||||
String _password;
|
String _password;
|
||||||
String _authType;
|
|
||||||
//int _uiViewsCount = 0;
|
//int _uiViewsCount = 0;
|
||||||
String _instanceHost;
|
String _instanceHost;
|
||||||
StreamSubscription _stateSubscription;
|
StreamSubscription _stateSubscription;
|
||||||
StreamSubscription _settingsSubscription;
|
StreamSubscription _settingsSubscription;
|
||||||
StreamSubscription _serviceCallSubscription;
|
StreamSubscription _serviceCallSubscription;
|
||||||
StreamSubscription _showEntityPageSubscription;
|
StreamSubscription _showEntityPageSubscription;
|
||||||
StreamSubscription _refreshDataSubscription;
|
|
||||||
StreamSubscription _showErrorSubscription;
|
StreamSubscription _showErrorSubscription;
|
||||||
bool _settingsLoaded = false;
|
bool _settingsLoaded = false;
|
||||||
bool _accountMenuExpanded = false;
|
bool _accountMenuExpanded = false;
|
||||||
@ -199,8 +207,7 @@ class _MainPageState extends State<MainPage> with WidgetsBindingObserver {
|
|||||||
_webSocketApiEndpoint = "${prefs.getString('hassio-protocol')}://$domain:$port/api/websocket";
|
_webSocketApiEndpoint = "${prefs.getString('hassio-protocol')}://$domain:$port/api/websocket";
|
||||||
homeAssistantWebHost = "${prefs.getString('hassio-res-protocol')}://$domain:$port";
|
homeAssistantWebHost = "${prefs.getString('hassio-res-protocol')}://$domain:$port";
|
||||||
_password = prefs.getString('hassio-password');
|
_password = prefs.getString('hassio-password');
|
||||||
_authType = prefs.getString('hassio-auth-type');
|
_useLovelaceUI = prefs.getBool('use-lovelace') ?? true;
|
||||||
_useLovelaceUI = prefs.getBool('use-lovelace') ?? false;
|
|
||||||
if ((domain == null) || (port == null) || (_password == null) ||
|
if ((domain == null) || (port == null) || (_password == null) ||
|
||||||
(domain.length == 0) || (port.length == 0) || (_password.length == 0)) {
|
(domain.length == 0) || (port.length == 0) || (_password.length == 0)) {
|
||||||
throw("Check connection settings");
|
throw("Check connection settings");
|
||||||
@ -235,12 +242,6 @@ class _MainPageState extends State<MainPage> with WidgetsBindingObserver {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
if (_refreshDataSubscription == null) {
|
|
||||||
_refreshDataSubscription = eventBus.on<RefreshDataEvent>().listen((event){
|
|
||||||
_refreshData();
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
if (_showErrorSubscription == null) {
|
if (_showErrorSubscription == null) {
|
||||||
_showErrorSubscription = eventBus.on<ShowErrorEvent>().listen((event){
|
_showErrorSubscription = eventBus.on<ShowErrorEvent>().listen((event){
|
||||||
_showErrorBottomBar(message: event.text, errorCode: event.errorCode);
|
_showErrorBottomBar(message: event.text, errorCode: event.errorCode);
|
||||||
@ -249,7 +250,7 @@ class _MainPageState extends State<MainPage> with WidgetsBindingObserver {
|
|||||||
}
|
}
|
||||||
|
|
||||||
_refreshData() async {
|
_refreshData() async {
|
||||||
_homeAssistant.updateSettings(_webSocketApiEndpoint, _password, _authType, _useLovelaceUI);
|
_homeAssistant.updateSettings(_webSocketApiEndpoint, _password, _useLovelaceUI);
|
||||||
_hideBottomBar();
|
_hideBottomBar();
|
||||||
_showInfoBottomBar(progress: true,);
|
_showInfoBottomBar(progress: true,);
|
||||||
await _homeAssistant.fetch().then((result) {
|
await _homeAssistant.fetch().then((result) {
|
||||||
@ -340,6 +341,14 @@ class _MainPageState extends State<MainPage> with WidgetsBindingObserver {
|
|||||||
]);
|
]);
|
||||||
} else {
|
} else {
|
||||||
menuItems.addAll([
|
menuItems.addAll([
|
||||||
|
new ListTile(
|
||||||
|
leading: Icon(Icons.settings),
|
||||||
|
title: Text("Configuration"),
|
||||||
|
onTap: () {
|
||||||
|
Navigator.of(context).pop();
|
||||||
|
Navigator.of(context).pushNamed('/configuration');
|
||||||
|
},
|
||||||
|
),
|
||||||
new ListTile(
|
new ListTile(
|
||||||
leading: Icon(Icons.insert_drive_file),
|
leading: Icon(Icons.insert_drive_file),
|
||||||
title: Text("Log"),
|
title: Text("Log"),
|
||||||
@ -362,10 +371,10 @@ class _MainPageState extends State<MainPage> with WidgetsBindingObserver {
|
|||||||
GestureDetector(
|
GestureDetector(
|
||||||
onTap: () {
|
onTap: () {
|
||||||
Navigator.of(context).pop();
|
Navigator.of(context).pop();
|
||||||
HAUtils.launchURL("http://www.keyboardcrumbs.io/");
|
HAUtils.launchURL("http://ha-client.homemade.systems/");
|
||||||
},
|
},
|
||||||
child: Text(
|
child: Text(
|
||||||
"www.keyboardcrumbs.io",
|
"ha-client.homemade.systems",
|
||||||
style: TextStyle(
|
style: TextStyle(
|
||||||
color: Colors.blue,
|
color: Colors.blue,
|
||||||
decoration: TextDecoration.underline
|
decoration: TextDecoration.underline
|
||||||
@ -374,8 +383,7 @@ class _MainPageState extends State<MainPage> with WidgetsBindingObserver {
|
|||||||
)
|
)
|
||||||
],
|
],
|
||||||
applicationName: appName,
|
applicationName: appName,
|
||||||
applicationVersion: appVersion,
|
applicationVersion: appVersion
|
||||||
applicationLegalese: "Keyboard Crumbs",
|
|
||||||
)
|
)
|
||||||
]);
|
]);
|
||||||
}
|
}
|
||||||
@ -519,6 +527,26 @@ class _MainPageState extends State<MainPage> with WidgetsBindingObserver {
|
|||||||
pinned: true,
|
pinned: true,
|
||||||
primary: true,
|
primary: true,
|
||||||
title: Text(_homeAssistant != null ? _homeAssistant.locationName : ""),
|
title: Text(_homeAssistant != null ? _homeAssistant.locationName : ""),
|
||||||
|
actions: <Widget>[
|
||||||
|
IconButton(
|
||||||
|
icon: Icon(MaterialDesignIcons.createIconDataFromIconName(
|
||||||
|
"mdi:dots-vertical"), color: Colors.white,),
|
||||||
|
onPressed: () {
|
||||||
|
showMenu(
|
||||||
|
position: RelativeRect.fromLTRB(MediaQuery.of(context).size.width, 70.0, 0.0, 0.0),
|
||||||
|
context: context,
|
||||||
|
items: [PopupMenuItem<String>(
|
||||||
|
child: new Text("Reload"),
|
||||||
|
value: "reload",
|
||||||
|
)]
|
||||||
|
).then((String val) {
|
||||||
|
if (val == "reload") {
|
||||||
|
_refreshData();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
)
|
||||||
|
],
|
||||||
leading: IconButton(
|
leading: IconButton(
|
||||||
icon: Icon(Icons.menu),
|
icon: Icon(Icons.menu),
|
||||||
onPressed: () {
|
onPressed: () {
|
||||||
@ -634,7 +662,6 @@ class _MainPageState extends State<MainPage> with WidgetsBindingObserver {
|
|||||||
if (_settingsSubscription != null) _settingsSubscription.cancel();
|
if (_settingsSubscription != null) _settingsSubscription.cancel();
|
||||||
if (_serviceCallSubscription != null) _serviceCallSubscription.cancel();
|
if (_serviceCallSubscription != null) _serviceCallSubscription.cancel();
|
||||||
if (_showEntityPageSubscription != null) _showEntityPageSubscription.cancel();
|
if (_showEntityPageSubscription != null) _showEntityPageSubscription.cancel();
|
||||||
if (_refreshDataSubscription != null) _refreshDataSubscription.cancel();
|
|
||||||
if (_showErrorSubscription != null) _showErrorSubscription.cancel();
|
if (_showErrorSubscription != null) _showErrorSubscription.cancel();
|
||||||
_homeAssistant.disconnect();
|
_homeAssistant.disconnect();
|
||||||
super.dispose();
|
super.dispose();
|
||||||
|
@ -24,7 +24,14 @@ class MaterialDesignIcons {
|
|||||||
"cover.opening": "mdi:window-open",
|
"cover.opening": "mdi:window-open",
|
||||||
"lock.locked": "mdi:lock",
|
"lock.locked": "mdi:lock",
|
||||||
"lock.unlocked": "mdi:lock-open",
|
"lock.unlocked": "mdi:lock-open",
|
||||||
"fan": "mdi:fan"
|
"fan": "mdi:fan",
|
||||||
|
"alarm_control_panel.disarmed" : "mdi:bell-outline",
|
||||||
|
"alarm_control_panel.armed_home" : "mdi:bell-plus",
|
||||||
|
"alarm_control_panel.armed_away" : "mdi:bell",
|
||||||
|
"alarm_control_panel.armed_night" : "mdi:bell-sleep",
|
||||||
|
"alarm_control_panel.armed_custom_bypass" : "mdi:bell",
|
||||||
|
"alarm_control_panel.triggered" : "mdi:bell-ring",
|
||||||
|
"alarm_control_panel" : "mdi:bell"
|
||||||
};
|
};
|
||||||
|
|
||||||
static Map _defaultIconsByDeviceClass = {
|
static Map _defaultIconsByDeviceClass = {
|
||||||
|
@ -18,10 +18,8 @@ class _ConnectionSettingsPageState extends State<ConnectionSettingsPage> {
|
|||||||
String _newHassioPassword = "";
|
String _newHassioPassword = "";
|
||||||
String _socketProtocol = "wss";
|
String _socketProtocol = "wss";
|
||||||
String _newSocketProtocol = "wss";
|
String _newSocketProtocol = "wss";
|
||||||
String _authType = "access_token";
|
bool _useLovelace = true;
|
||||||
String _newAuthType = "access_token";
|
bool _newUseLovelace = true;
|
||||||
bool _useLovelace = false;
|
|
||||||
bool _newUseLovelace = false;
|
|
||||||
|
|
||||||
@override
|
@override
|
||||||
void initState() {
|
void initState() {
|
||||||
@ -37,11 +35,10 @@ class _ConnectionSettingsPageState extends State<ConnectionSettingsPage> {
|
|||||||
_hassioPort = _newHassioPort = prefs.getString("hassio-port") ?? "";
|
_hassioPort = _newHassioPort = prefs.getString("hassio-port") ?? "";
|
||||||
_hassioPassword = _newHassioPassword = prefs.getString("hassio-password") ?? "";
|
_hassioPassword = _newHassioPassword = prefs.getString("hassio-password") ?? "";
|
||||||
_socketProtocol = _newSocketProtocol = prefs.getString("hassio-protocol") ?? 'wss';
|
_socketProtocol = _newSocketProtocol = prefs.getString("hassio-protocol") ?? 'wss';
|
||||||
_authType = _newAuthType = prefs.getString("hassio-auth-type") ?? 'access_token';
|
|
||||||
try {
|
try {
|
||||||
_useLovelace = _newUseLovelace = prefs.getBool("use-lovelace") ?? false;
|
_useLovelace = _newUseLovelace = prefs.getBool("use-lovelace") ?? true;
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
_useLovelace = _newUseLovelace = false;
|
_useLovelace = _newUseLovelace = true;
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
@ -51,7 +48,6 @@ class _ConnectionSettingsPageState extends State<ConnectionSettingsPage> {
|
|||||||
(_newHassioPort != _hassioPort) ||
|
(_newHassioPort != _hassioPort) ||
|
||||||
(_newHassioDomain != _hassioDomain) ||
|
(_newHassioDomain != _hassioDomain) ||
|
||||||
(_newSocketProtocol != _socketProtocol) ||
|
(_newSocketProtocol != _socketProtocol) ||
|
||||||
(_newAuthType != _authType) ||
|
|
||||||
(_newUseLovelace != _useLovelace));
|
(_newUseLovelace != _useLovelace));
|
||||||
|
|
||||||
}
|
}
|
||||||
@ -66,7 +62,6 @@ class _ConnectionSettingsPageState extends State<ConnectionSettingsPage> {
|
|||||||
prefs.setString("hassio-password", _newHassioPassword);
|
prefs.setString("hassio-password", _newHassioPassword);
|
||||||
prefs.setString("hassio-protocol", _newSocketProtocol);
|
prefs.setString("hassio-protocol", _newSocketProtocol);
|
||||||
prefs.setString("hassio-res-protocol", _newSocketProtocol == "wss" ? "https" : "http");
|
prefs.setString("hassio-res-protocol", _newSocketProtocol == "wss" ? "https" : "http");
|
||||||
prefs.setString("hassio-auth-type", _newAuthType);
|
|
||||||
prefs.setBool("use-lovelace", _newUseLovelace);
|
prefs.setBool("use-lovelace", _newUseLovelace);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -154,33 +149,9 @@ class _ConnectionSettingsPageState extends State<ConnectionSettingsPage> {
|
|||||||
"Try ports 80 and 443 if default is not working and you don't know why.",
|
"Try ports 80 and 443 if default is not working and you don't know why.",
|
||||||
style: TextStyle(color: Colors.grey),
|
style: TextStyle(color: Colors.grey),
|
||||||
),
|
),
|
||||||
new Row(
|
|
||||||
mainAxisSize: MainAxisSize.min,
|
|
||||||
children: [
|
|
||||||
Flexible(
|
|
||||||
child: Text(
|
|
||||||
"Login with access token (HA >= 0.78.0)",
|
|
||||||
softWrap: true,
|
|
||||||
maxLines: 2,
|
|
||||||
),
|
|
||||||
),
|
|
||||||
Switch(
|
|
||||||
value: (_newAuthType == "access_token"),
|
|
||||||
onChanged: (value) {
|
|
||||||
setState(() {
|
|
||||||
_newAuthType = value ? "access_token" : "api_password";
|
|
||||||
});
|
|
||||||
},
|
|
||||||
)
|
|
||||||
],
|
|
||||||
),
|
|
||||||
new Text(
|
|
||||||
"You should use access token for HA >= 0.84.1. Legacy password will not work there.",
|
|
||||||
style: TextStyle(color: Colors.grey),
|
|
||||||
),
|
|
||||||
new TextField(
|
new TextField(
|
||||||
decoration: InputDecoration(
|
decoration: InputDecoration(
|
||||||
labelText: _newAuthType == "access_token" ? "Access token" : "API password"
|
labelText: "Access token"
|
||||||
),
|
),
|
||||||
controller: new TextEditingController.fromValue(
|
controller: new TextEditingController.fromValue(
|
||||||
new TextEditingValue(
|
new TextEditingValue(
|
||||||
|
@ -12,6 +12,8 @@ class HACard {
|
|||||||
bool showEmpty;
|
bool showEmpty;
|
||||||
int columnsCount;
|
int columnsCount;
|
||||||
List stateFilter;
|
List stateFilter;
|
||||||
|
List states;
|
||||||
|
String content;
|
||||||
|
|
||||||
HACard({
|
HACard({
|
||||||
this.name,
|
this.name,
|
||||||
@ -22,6 +24,8 @@ class HACard {
|
|||||||
this.showState: true,
|
this.showState: true,
|
||||||
this.stateFilter: const [],
|
this.stateFilter: const [],
|
||||||
this.showEmpty: true,
|
this.showEmpty: true,
|
||||||
|
this.content,
|
||||||
|
this.states,
|
||||||
@required this.type
|
@required this.type
|
||||||
});
|
});
|
||||||
|
|
||||||
|
@ -1,8 +1,8 @@
|
|||||||
part of '../main.dart';
|
part of '../main.dart';
|
||||||
|
|
||||||
class Sizes {
|
class Sizes {
|
||||||
static const rightWidgetPadding = 14.0;
|
static const rightWidgetPadding = 16.0;
|
||||||
static const leftWidgetPadding = 8.0;
|
static const leftWidgetPadding = 16.0;
|
||||||
static const buttonPadding = 4.0;
|
static const buttonPadding = 4.0;
|
||||||
static const extendedWidgetHeight = 50.0;
|
static const extendedWidgetHeight = 50.0;
|
||||||
static const iconSize = 28.0;
|
static const iconSize = 28.0;
|
||||||
|
@ -3,14 +3,18 @@ part of '../main.dart';
|
|||||||
class CardHeaderWidget extends StatelessWidget {
|
class CardHeaderWidget extends StatelessWidget {
|
||||||
|
|
||||||
final String name;
|
final String name;
|
||||||
|
final Widget trailing;
|
||||||
|
final Widget subtitle;
|
||||||
|
|
||||||
const CardHeaderWidget({Key key, this.name}) : super(key: key);
|
const CardHeaderWidget({Key key, this.name, this.trailing, this.subtitle}) : super(key: key);
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
var result;
|
var result;
|
||||||
if ((name != null) && (name.trim().length > 0)) {
|
if ((name != null) && (name.trim().length > 0)) {
|
||||||
result = new ListTile(
|
result = new ListTile(
|
||||||
|
trailing: trailing,
|
||||||
|
subtitle: subtitle,
|
||||||
title: Text("$name",
|
title: Text("$name",
|
||||||
textAlign: TextAlign.left,
|
textAlign: TextAlign.left,
|
||||||
overflow: TextOverflow.ellipsis,
|
overflow: TextOverflow.ellipsis,
|
||||||
|
@ -33,6 +33,14 @@ class CardWidget extends StatelessWidget {
|
|||||||
return _buildEntityButtonCard(context);
|
return _buildEntityButtonCard(context);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
case CardType.markdown: {
|
||||||
|
return _buildMarkdownCard(context);
|
||||||
|
}
|
||||||
|
|
||||||
|
case CardType.alarmPanel: {
|
||||||
|
return _buildAlarmPanelCard(context);
|
||||||
|
}
|
||||||
|
|
||||||
case CardType.horizontalStack: {
|
case CardType.horizontalStack: {
|
||||||
if (card.childCards.isNotEmpty) {
|
if (card.childCards.isNotEmpty) {
|
||||||
List<Widget> children = [];
|
List<Widget> children = [];
|
||||||
@ -109,6 +117,73 @@ class CardWidget extends StatelessWidget {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Widget _buildMarkdownCard(BuildContext context) {
|
||||||
|
if (card.content == null) {
|
||||||
|
return Container(height: 0.0, width: 0.0,);
|
||||||
|
}
|
||||||
|
List<Widget> body = [];
|
||||||
|
body.add(CardHeaderWidget(name: card.name));
|
||||||
|
body.add(MarkdownBody(data: card.content));
|
||||||
|
return Card(
|
||||||
|
child: Padding(
|
||||||
|
padding: EdgeInsets.fromLTRB(Sizes.leftWidgetPadding, Sizes.rowPadding, Sizes.rightWidgetPadding, Sizes.rowPadding),
|
||||||
|
child: new Column(mainAxisSize: MainAxisSize.min, children: body),
|
||||||
|
)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
Widget _buildAlarmPanelCard(BuildContext context) {
|
||||||
|
if (card.linkedEntityWrapper == null || card.linkedEntityWrapper.entity == null) {
|
||||||
|
return Container(width: 0, height: 0,);
|
||||||
|
} else {
|
||||||
|
List<Widget> body = [];
|
||||||
|
body.add(CardHeaderWidget(
|
||||||
|
name: card.name ?? "",
|
||||||
|
subtitle: Text("${card.linkedEntityWrapper.entity.displayState}",
|
||||||
|
style: TextStyle(
|
||||||
|
color: Colors.grey
|
||||||
|
),
|
||||||
|
),
|
||||||
|
trailing: Row(
|
||||||
|
mainAxisSize: MainAxisSize.min,
|
||||||
|
mainAxisAlignment: MainAxisAlignment.end,
|
||||||
|
children: [
|
||||||
|
EntityIcon(
|
||||||
|
iconSize: 50.0,
|
||||||
|
),
|
||||||
|
Container(
|
||||||
|
width: 26.0,
|
||||||
|
child: IconButton(
|
||||||
|
padding: EdgeInsets.all(0.0),
|
||||||
|
alignment: Alignment.centerRight,
|
||||||
|
icon: Icon(MaterialDesignIcons.createIconDataFromIconName(
|
||||||
|
"mdi:dots-vertical")),
|
||||||
|
onPressed: () => eventBus.fire(new ShowEntityPageEvent(card.linkedEntityWrapper.entity))
|
||||||
|
)
|
||||||
|
)
|
||||||
|
]
|
||||||
|
),
|
||||||
|
));
|
||||||
|
body.add(
|
||||||
|
AlarmControlPanelControlsWidget(
|
||||||
|
extended: true,
|
||||||
|
states: card.states,
|
||||||
|
)
|
||||||
|
);
|
||||||
|
return Card(
|
||||||
|
child: EntityModel(
|
||||||
|
entityWrapper: card.linkedEntityWrapper,
|
||||||
|
handleTap: null,
|
||||||
|
child: Column(
|
||||||
|
crossAxisAlignment: CrossAxisAlignment.center,
|
||||||
|
mainAxisSize: MainAxisSize.min,
|
||||||
|
children: body
|
||||||
|
)
|
||||||
|
)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
Widget _buildGlanceCard(BuildContext context) {
|
Widget _buildGlanceCard(BuildContext context) {
|
||||||
List<EntityWrapper> entitiesToShow = card.getEntitiesToShow();
|
List<EntityWrapper> entitiesToShow = card.getEntitiesToShow();
|
||||||
if (entitiesToShow.isEmpty && !card.showEmpty) {
|
if (entitiesToShow.isEmpty && !card.showEmpty) {
|
||||||
|
@ -17,29 +17,17 @@ class ViewWidget extends StatefulWidget {
|
|||||||
|
|
||||||
class ViewWidgetState extends State<ViewWidget> {
|
class ViewWidgetState extends State<ViewWidget> {
|
||||||
|
|
||||||
StreamSubscription _refreshDataSubscription;
|
|
||||||
Completer _refreshCompleter;
|
|
||||||
|
|
||||||
@override
|
@override
|
||||||
void initState() {
|
void initState() {
|
||||||
super.initState();
|
super.initState();
|
||||||
_refreshDataSubscription = eventBus.on<RefreshDataFinishedEvent>().listen((event) {
|
|
||||||
if ((_refreshCompleter != null) && (!_refreshCompleter.isCompleted)) {
|
|
||||||
_refreshCompleter.complete();
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
return RefreshIndicator(
|
return ListView(
|
||||||
color: Colors.amber,
|
|
||||||
child: ListView(
|
|
||||||
padding: EdgeInsets.all(0.0),
|
padding: EdgeInsets.all(0.0),
|
||||||
physics: const AlwaysScrollableScrollPhysics(),
|
//physics: const AlwaysScrollableScrollPhysics(),
|
||||||
children: _buildChildren(context),
|
children: _buildChildren(context),
|
||||||
),
|
|
||||||
onRefresh: () => _refreshData(),
|
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -57,12 +45,22 @@ class ViewWidgetState extends State<ViewWidget> {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
List<Widget> cards = [];
|
||||||
widget.view.cards.forEach((HACard card){
|
widget.view.cards.forEach((HACard card){
|
||||||
result.add(
|
cards.add(
|
||||||
card.build(context)
|
ConstrainedBox(
|
||||||
|
constraints: BoxConstraints(maxWidth: 500),
|
||||||
|
child: card.build(context),
|
||||||
|
)
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
result.add(
|
||||||
|
Column (
|
||||||
|
children: cards,
|
||||||
|
)
|
||||||
|
);
|
||||||
|
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -76,19 +74,8 @@ class ViewWidgetState extends State<ViewWidget> {
|
|||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
Future _refreshData() {
|
|
||||||
if ((_refreshCompleter != null) && (!_refreshCompleter.isCompleted)) {
|
|
||||||
Logger.d("Previous data refresh is still in progress");
|
|
||||||
} else {
|
|
||||||
_refreshCompleter = Completer();
|
|
||||||
eventBus.fire(RefreshDataEvent());
|
|
||||||
}
|
|
||||||
return _refreshCompleter.future;
|
|
||||||
}
|
|
||||||
|
|
||||||
@override
|
@override
|
||||||
void dispose() {
|
void dispose() {
|
||||||
_refreshDataSubscription.cancel();
|
|
||||||
super.dispose();
|
super.dispose();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -73,10 +73,6 @@ class SettingsChangedEvent {
|
|||||||
SettingsChangedEvent(this.reconnect);
|
SettingsChangedEvent(this.reconnect);
|
||||||
}
|
}
|
||||||
|
|
||||||
class RefreshDataEvent {
|
|
||||||
RefreshDataEvent();
|
|
||||||
}
|
|
||||||
|
|
||||||
class RefreshDataFinishedEvent {
|
class RefreshDataFinishedEvent {
|
||||||
RefreshDataFinishedEvent();
|
RefreshDataFinishedEvent();
|
||||||
}
|
}
|
||||||
|
89
pubspec.lock
89
pubspec.lock
@ -7,7 +7,7 @@ packages:
|
|||||||
name: archive
|
name: archive
|
||||||
url: "https://pub.dartlang.org"
|
url: "https://pub.dartlang.org"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "2.0.4"
|
version: "2.0.8"
|
||||||
args:
|
args:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
@ -35,7 +35,7 @@ packages:
|
|||||||
name: cached_network_image
|
name: cached_network_image
|
||||||
url: "https://pub.dartlang.org"
|
url: "https://pub.dartlang.org"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "0.5.1"
|
version: "0.6.0+1"
|
||||||
charcode:
|
charcode:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
@ -49,14 +49,14 @@ packages:
|
|||||||
name: charts_common
|
name: charts_common
|
||||||
url: "https://pub.dartlang.org"
|
url: "https://pub.dartlang.org"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "0.5.0"
|
version: "0.6.0"
|
||||||
charts_flutter:
|
charts_flutter:
|
||||||
dependency: "direct main"
|
dependency: "direct main"
|
||||||
description:
|
description:
|
||||||
name: charts_flutter
|
name: charts_flutter
|
||||||
url: "https://pub.dartlang.org"
|
url: "https://pub.dartlang.org"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "0.5.0"
|
version: "0.6.0"
|
||||||
collection:
|
collection:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
@ -70,7 +70,7 @@ packages:
|
|||||||
name: convert
|
name: convert
|
||||||
url: "https://pub.dartlang.org"
|
url: "https://pub.dartlang.org"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "2.0.2"
|
version: "2.1.1"
|
||||||
crypto:
|
crypto:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
@ -83,7 +83,7 @@ packages:
|
|||||||
description:
|
description:
|
||||||
path: "."
|
path: "."
|
||||||
ref: HEAD
|
ref: HEAD
|
||||||
resolved-ref: e26916e095244a7e5ea61315b030d298d127ed26
|
resolved-ref: a7ed88a4793e094a4d5d5c2d88a89e55510accde
|
||||||
url: "https://github.com/MarkOSullivan94/dart_config.git"
|
url: "https://github.com/MarkOSullivan94/dart_config.git"
|
||||||
source: git
|
source: git
|
||||||
version: "0.5.0"
|
version: "0.5.0"
|
||||||
@ -93,14 +93,14 @@ packages:
|
|||||||
name: date_format
|
name: date_format
|
||||||
url: "https://pub.dartlang.org"
|
url: "https://pub.dartlang.org"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "1.0.5"
|
version: "1.0.6"
|
||||||
event_bus:
|
event_bus:
|
||||||
dependency: "direct main"
|
dependency: "direct main"
|
||||||
description:
|
description:
|
||||||
name: event_bus
|
name: event_bus
|
||||||
url: "https://pub.dartlang.org"
|
url: "https://pub.dartlang.org"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "1.0.1"
|
version: "1.0.3"
|
||||||
flutter:
|
flutter:
|
||||||
dependency: "direct main"
|
dependency: "direct main"
|
||||||
description: flutter
|
description: flutter
|
||||||
@ -112,14 +112,7 @@ packages:
|
|||||||
name: flutter_cache_manager
|
name: flutter_cache_manager
|
||||||
url: "https://pub.dartlang.org"
|
url: "https://pub.dartlang.org"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "0.2.0+1"
|
version: "0.3.0"
|
||||||
flutter_colorpicker:
|
|
||||||
dependency: "direct main"
|
|
||||||
description:
|
|
||||||
name: flutter_colorpicker
|
|
||||||
url: "https://pub.dartlang.org"
|
|
||||||
source: hosted
|
|
||||||
version: "0.1.0"
|
|
||||||
flutter_launcher_icons:
|
flutter_launcher_icons:
|
||||||
dependency: "direct dev"
|
dependency: "direct dev"
|
||||||
description:
|
description:
|
||||||
@ -127,6 +120,20 @@ packages:
|
|||||||
url: "https://pub.dartlang.org"
|
url: "https://pub.dartlang.org"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "0.7.0"
|
version: "0.7.0"
|
||||||
|
flutter_markdown:
|
||||||
|
dependency: "direct main"
|
||||||
|
description:
|
||||||
|
name: flutter_markdown
|
||||||
|
url: "https://pub.dartlang.org"
|
||||||
|
source: hosted
|
||||||
|
version: "0.2.0"
|
||||||
|
flutter_svg:
|
||||||
|
dependency: "direct main"
|
||||||
|
description:
|
||||||
|
name: flutter_svg
|
||||||
|
url: "https://pub.dartlang.org"
|
||||||
|
source: hosted
|
||||||
|
version: "0.10.3"
|
||||||
flutter_test:
|
flutter_test:
|
||||||
dependency: "direct dev"
|
dependency: "direct dev"
|
||||||
description: flutter
|
description: flutter
|
||||||
@ -138,7 +145,7 @@ packages:
|
|||||||
name: http
|
name: http
|
||||||
url: "https://pub.dartlang.org"
|
url: "https://pub.dartlang.org"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "0.12.0"
|
version: "0.12.0+1"
|
||||||
http_parser:
|
http_parser:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
@ -152,7 +159,7 @@ packages:
|
|||||||
name: image
|
name: image
|
||||||
url: "https://pub.dartlang.org"
|
url: "https://pub.dartlang.org"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "2.0.5"
|
version: "2.0.7"
|
||||||
intl:
|
intl:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
@ -167,6 +174,13 @@ packages:
|
|||||||
url: "https://pub.dartlang.org"
|
url: "https://pub.dartlang.org"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "0.11.3+2"
|
version: "0.11.3+2"
|
||||||
|
markdown:
|
||||||
|
dependency: transitive
|
||||||
|
description:
|
||||||
|
name: markdown
|
||||||
|
url: "https://pub.dartlang.org"
|
||||||
|
source: hosted
|
||||||
|
version: "2.0.2"
|
||||||
matcher:
|
matcher:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
@ -188,20 +202,34 @@ packages:
|
|||||||
url: "https://pub.dartlang.org"
|
url: "https://pub.dartlang.org"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "1.6.2"
|
version: "1.6.2"
|
||||||
|
path_drawing:
|
||||||
|
dependency: transitive
|
||||||
|
description:
|
||||||
|
name: path_drawing
|
||||||
|
url: "https://pub.dartlang.org"
|
||||||
|
source: hosted
|
||||||
|
version: "0.4.0"
|
||||||
|
path_parsing:
|
||||||
|
dependency: transitive
|
||||||
|
description:
|
||||||
|
name: path_parsing
|
||||||
|
url: "https://pub.dartlang.org"
|
||||||
|
source: hosted
|
||||||
|
version: "0.1.3"
|
||||||
path_provider:
|
path_provider:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
name: path_provider
|
name: path_provider
|
||||||
url: "https://pub.dartlang.org"
|
url: "https://pub.dartlang.org"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "0.4.1"
|
version: "0.5.0+1"
|
||||||
petitparser:
|
petitparser:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
name: petitparser
|
name: petitparser
|
||||||
url: "https://pub.dartlang.org"
|
url: "https://pub.dartlang.org"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "2.0.2"
|
version: "2.1.1"
|
||||||
progress_indicators:
|
progress_indicators:
|
||||||
dependency: "direct main"
|
dependency: "direct main"
|
||||||
description:
|
description:
|
||||||
@ -222,7 +250,7 @@ packages:
|
|||||||
name: shared_preferences
|
name: shared_preferences
|
||||||
url: "https://pub.dartlang.org"
|
url: "https://pub.dartlang.org"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "0.4.3"
|
version: "0.5.1+1"
|
||||||
sky_engine:
|
sky_engine:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description: flutter
|
description: flutter
|
||||||
@ -235,6 +263,13 @@ packages:
|
|||||||
url: "https://pub.dartlang.org"
|
url: "https://pub.dartlang.org"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "1.4.1"
|
version: "1.4.1"
|
||||||
|
sqflite:
|
||||||
|
dependency: transitive
|
||||||
|
description:
|
||||||
|
name: sqflite
|
||||||
|
url: "https://pub.dartlang.org"
|
||||||
|
source: hosted
|
||||||
|
version: "1.1.0+1"
|
||||||
stack_trace:
|
stack_trace:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
@ -262,7 +297,7 @@ packages:
|
|||||||
name: synchronized
|
name: synchronized
|
||||||
url: "https://pub.dartlang.org"
|
url: "https://pub.dartlang.org"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "1.5.3"
|
version: "2.0.2+1"
|
||||||
term_glyph:
|
term_glyph:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
@ -290,14 +325,14 @@ packages:
|
|||||||
name: url_launcher
|
name: url_launcher
|
||||||
url: "https://pub.dartlang.org"
|
url: "https://pub.dartlang.org"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "4.0.2"
|
version: "5.0.1"
|
||||||
uuid:
|
uuid:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
name: uuid
|
name: uuid
|
||||||
url: "https://pub.dartlang.org"
|
url: "https://pub.dartlang.org"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "1.0.3"
|
version: "2.0.0"
|
||||||
vector_math:
|
vector_math:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
@ -318,7 +353,7 @@ packages:
|
|||||||
name: xml
|
name: xml
|
||||||
url: "https://pub.dartlang.org"
|
url: "https://pub.dartlang.org"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "3.2.3"
|
version: "3.2.5"
|
||||||
yaml:
|
yaml:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
@ -327,5 +362,5 @@ packages:
|
|||||||
source: hosted
|
source: hosted
|
||||||
version: "2.1.15"
|
version: "2.1.15"
|
||||||
sdks:
|
sdks:
|
||||||
dart: ">=2.0.0 <3.0.0"
|
dart: ">=2.1.0 <3.0.0"
|
||||||
flutter: ">=0.5.6 <2.0.0"
|
flutter: ">=0.7.3 <2.0.0"
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
name: hass_client
|
name: hass_client
|
||||||
description: Home Assistant Android Client
|
description: Home Assistant Android Client
|
||||||
|
|
||||||
version: 0.3.13+80
|
version: 0.4.4+95
|
||||||
|
|
||||||
environment:
|
environment:
|
||||||
sdk: ">=2.0.0-dev.68.0 <3.0.0"
|
sdk: ">=2.0.0-dev.68.0 <3.0.0"
|
||||||
@ -16,12 +16,9 @@ dependencies:
|
|||||||
cached_network_image: any
|
cached_network_image: any
|
||||||
url_launcher: any
|
url_launcher: any
|
||||||
date_format: any
|
date_format: any
|
||||||
flutter_colorpicker: any
|
|
||||||
charts_flutter: any
|
charts_flutter: any
|
||||||
|
flutter_markdown: any
|
||||||
# The following adds the Cupertino Icons font to your application.
|
flutter_svg: ^0.10.3
|
||||||
# Use with the CupertinoIcons class for iOS style icons.
|
|
||||||
#cupertino_icons: ^0.1.2
|
|
||||||
|
|
||||||
dev_dependencies:
|
dev_dependencies:
|
||||||
flutter_test:
|
flutter_test:
|
||||||
|
Reference in New Issue
Block a user