diff --git a/lib/entity_class/entity.class.dart b/lib/entity_class/entity.class.dart index 1d44773..c5a0125 100644 --- a/lib/entity_class/entity.class.dart +++ b/lib/entity_class/entity.class.dart @@ -13,7 +13,15 @@ class Entity { "default": Color.fromRGBO(223, 76, 30, 1.0), "binary_sensor": Color.fromRGBO(3, 155, 229, 1.0) }; - static List badgeDomains = ["alarm_control_panel", "binary_sensor", "device_tracker", "updater", "sun", "timer", "sensor"]; + static List badgeDomains = [ + "alarm_control_panel", + "binary_sensor", + "device_tracker", + "updater", + "sun", + "timer", + "sensor" + ]; double rightWidgetPadding = 14.0; double leftWidgetPadding = 8.0; @@ -70,9 +78,7 @@ class Entity { Widget buildDefaultWidget(BuildContext context) { return EntityModel( entity: this, - child: DefaultEntityContainer( - state: _buildStatePart(context) - ), + child: DefaultEntityContainer(state: _buildStatePart(context)), handleTap: true, ); } @@ -86,24 +92,23 @@ class Entity { } Widget _buildAdditionalControlsForPage(BuildContext context) { - return Container(width: 0.0, height: 0.0,); + return Container( + width: 0.0, + height: 0.0, + ); } Widget buildEntityPageWidget(BuildContext context) { return EntityModel( entity: this, - child: EntityPageContainer( - children: [ - DefaultEntityContainer( - state: _buildStatePartForPage(context) - ), - LastUpdatedWidget(), - Divider(), - _buildAdditionalControlsForPage(context), - Divider(), - EntityAttributesList() - ] - ), + child: EntityPageContainer(children: [ + DefaultEntityContainer(state: _buildStatePartForPage(context)), + LastUpdatedWidget(), + Divider(), + _buildAdditionalControlsForPage(context), + Divider(), + EntityAttributesList() + ]), handleTap: false, ); } @@ -151,7 +156,6 @@ class Entity { return "$v $text"; } } - } class SwitchEntity extends Entity { @@ -161,7 +165,6 @@ class SwitchEntity extends Entity { Widget _buildStatePart(BuildContext context) { return SwitchControlWidget(); } - } class ButtonEntity extends Entity { @@ -171,7 +174,6 @@ class ButtonEntity extends Entity { Widget _buildStatePart(BuildContext context) { return ButtonControlWidget(); } - } class TextEntity extends Entity { @@ -207,7 +209,9 @@ class SliderEntity extends Entity { //width: 200.0, child: Row( children: [ - SliderControlWidget(expanded: true,), + SliderControlWidget( + expanded: true, + ), SimpleEntityState(), ], ), @@ -221,22 +225,88 @@ class SliderEntity extends Entity { @override Widget _buildAdditionalControlsForPage(BuildContext context) { - return SliderControlWidget(expanded: false,); + return SliderControlWidget( + expanded: false, + ); } - } class ClimateEntity extends Entity { - @override double widgetHeight = 38.0; - List get operationList => (attributes["operation_list"] as List).cast(); + static const SUPPORT_TARGET_TEMPERATURE = 1; + static const SUPPORT_TARGET_TEMPERATURE_HIGH = 2; + static const SUPPORT_TARGET_TEMPERATURE_LOW = 4; + static const SUPPORT_TARGET_HUMIDITY = 8; + static const SUPPORT_TARGET_HUMIDITY_HIGH = 16; + static const SUPPORT_TARGET_HUMIDITY_LOW = 32; + static const SUPPORT_FAN_MODE = 64; + static const SUPPORT_OPERATION_MODE = 128; + static const SUPPORT_HOLD_MODE = 256; + static const SUPPORT_SWING_MODE = 512; + static const SUPPORT_AWAY_MODE = 1024; + static const SUPPORT_AUX_HEAT = 2048; + static const SUPPORT_ON_OFF = 4096; + + bool get supportTargetTemperature => ((attributes["supported_features"] & + ClimateEntity.SUPPORT_TARGET_TEMPERATURE) == + ClimateEntity.SUPPORT_TARGET_TEMPERATURE); + bool get supportTargetTemperatureHigh => ((attributes["supported_features"] & + ClimateEntity.SUPPORT_TARGET_TEMPERATURE_HIGH) == + ClimateEntity.SUPPORT_TARGET_TEMPERATURE_HIGH); + bool get supportTargetTemperatureLow => ((attributes["supported_features"] & + ClimateEntity.SUPPORT_TARGET_TEMPERATURE_LOW) == + ClimateEntity.SUPPORT_TARGET_TEMPERATURE_LOW); + bool get supportTargetHumidity => ((attributes["supported_features"] & + ClimateEntity.SUPPORT_TARGET_HUMIDITY) == + ClimateEntity.SUPPORT_TARGET_HUMIDITY); + bool get supportTargetHumidityHigh => ((attributes["supported_features"] & + ClimateEntity.SUPPORT_TARGET_HUMIDITY_HIGH) == + ClimateEntity.SUPPORT_TARGET_HUMIDITY_HIGH); + bool get supportTargetHumidityLow => ((attributes["supported_features"] & + ClimateEntity.SUPPORT_TARGET_HUMIDITY_LOW) == + ClimateEntity.SUPPORT_TARGET_HUMIDITY_LOW); + bool get supportFanMode => + ((attributes["supported_features"] & ClimateEntity.SUPPORT_FAN_MODE) == + ClimateEntity.SUPPORT_FAN_MODE); + bool get supportOperationMode => ((attributes["supported_features"] & + ClimateEntity.SUPPORT_OPERATION_MODE) == + ClimateEntity.SUPPORT_OPERATION_MODE); + bool get supportHoldMode => + ((attributes["supported_features"] & ClimateEntity.SUPPORT_HOLD_MODE) == + ClimateEntity.SUPPORT_HOLD_MODE); + bool get supportSwingMode => + ((attributes["supported_features"] & ClimateEntity.SUPPORT_SWING_MODE) == + ClimateEntity.SUPPORT_SWING_MODE); + bool get supportAwayMode => + ((attributes["supported_features"] & ClimateEntity.SUPPORT_AWAY_MODE) == + ClimateEntity.SUPPORT_AWAY_MODE); + bool get supportAuxHeat => + ((attributes["supported_features"] & ClimateEntity.SUPPORT_AUX_HEAT) == + ClimateEntity.SUPPORT_AUX_HEAT); + bool get supportOnOff => + ((attributes["supported_features"] & ClimateEntity.SUPPORT_ON_OFF) == + ClimateEntity.SUPPORT_ON_OFF); + + List get operationList => attributes["operation_list"] != null + ? (attributes["operation_list"] as List).cast() + : null; + List get fanList => attributes["fan_list"] != null + ? (attributes["fan_list"] as List).cast() + : null; + List get swingList => attributes["swing_list"] != null + ? (attributes["swing_list"] as List).cast() + : null; double get temperature => _getTemperature(); double get targetHigh => _getTargetHigh(); double get targetLow => _getTargetLow(); - String get operationMode => attributes['operation_mode'] ?? ""; + String get operationMode => attributes['operation_mode']; + String get fanMode => attributes['fan_mode']; + String get swingMode => attributes['swing_mode']; bool get awayMode => attributes['away_mode'] == "on"; + bool get isOff => state == "off"; + bool get auxHeat => attributes['aux_heat'] == "on"; ClimateEntity(Map rawData) : super(rawData); @@ -285,8 +355,9 @@ class ClimateEntity extends Entity { } class SelectEntity extends Entity { - - List get listOptions => attributes["options"]!= null ? (attributes["options"] as List).cast() : []; + List get listOptions => attributes["options"] != null + ? (attributes["options"] as List).cast() + : []; SelectEntity(Map rawData) : super(rawData); @@ -294,11 +365,9 @@ class SelectEntity extends Entity { Widget _buildStatePart(BuildContext context) { return SelectControlWidget(); } - } class DateTimeEntity extends Entity { - bool get hasDate => attributes["has_date"] ?? false; bool get hasTime => attributes["has_time"] ?? false; int get year => attributes["year"] ?? 1970; @@ -318,7 +387,8 @@ class DateTimeEntity extends Entity { } DateTime _getDateTimeState() { - return DateTime(this.year, this.month, this.day, this.hour, this.minute, this.second); + return DateTime( + this.year, this.month, this.day, this.hour, this.minute, this.second); } String _getFormattedState() { @@ -327,16 +397,13 @@ class DateTimeEntity extends Entity { formattedState += formatDate(dateTimeState, [M, ' ', d, ', ', yyyy]); } if (this.hasTime) { - formattedState += " "+formatDate(dateTimeState, [HH, ':', nn]); + formattedState += " " + formatDate(dateTimeState, [HH, ':', nn]); } return formattedState; } void setNewState(newValue) { - eventBus.fire(new ServiceCallEvent(domain, "set_datetime",entityId, - newValue)); + eventBus + .fire(new ServiceCallEvent(domain, "set_datetime", entityId, newValue)); } - } - - diff --git a/lib/entity_class/stateful_widgets.dart b/lib/entity_class/stateful_widgets.dart index 09f84da..cbf5199 100644 --- a/lib/entity_class/stateful_widgets.dart +++ b/lib/entity_class/stateful_widgets.dart @@ -238,14 +238,27 @@ class _ClimateControlWidgetState extends State { bool _changedHere = false; Timer _resetTimer; double _tmpTemperature = 0.0; - String _tmpOperationMode = ""; + double _tmpTargetLow = 0.0; + double _tmpTargetHigh = 0.0; + String _tmpOperationMode; + String _tmpFanMode; + String _tmpSwingMode; bool _tmpAwayMode = false; + bool _tmpIsOff = false; + bool _tmpAuxHeat = false; double _temperatureStep = 0.2; void _resetVars(ClimateEntity entity) { _tmpTemperature = entity.temperature; + _tmpTargetHigh = entity.targetHigh; + _tmpTargetLow = entity.targetLow; _tmpOperationMode = entity.operationMode; + _tmpFanMode = entity.fanMode; + _tmpSwingMode = entity.swingMode; _tmpAwayMode = entity.awayMode; + _tmpIsOff = entity.isOff; + _tmpAuxHeat = entity.auxHeat; + _showPending = false; _changedHere = false; } @@ -260,6 +273,26 @@ class _ClimateControlWidgetState extends State { _setTemperature(entity); } + void _targetLowUp(ClimateEntity entity) { + _tmpTargetLow += _temperatureStep; + _setTargetTemp(entity); + } + + void _targetLowDown(ClimateEntity entity) { + _tmpTargetLow -= _temperatureStep; + _setTargetTemp(entity); + } + + void _targetHighUp(ClimateEntity entity) { + _tmpTargetHigh += _temperatureStep; + _setTargetTemp(entity); + } + + void _targetHighDown(ClimateEntity entity) { + _tmpTargetHigh -= _temperatureStep; + _setTargetTemp(entity); + } + void _setTemperature(ClimateEntity entity) { setState(() { _tmpTemperature = double.parse(_tmpTemperature.toStringAsFixed(1)); @@ -269,6 +302,16 @@ class _ClimateControlWidgetState extends State { }); } + void _setTargetTemp(ClimateEntity entity) { + setState(() { + _tmpTargetLow = double.parse(_tmpTargetLow.toStringAsFixed(1)); + _tmpTargetHigh = double.parse(_tmpTargetHigh.toStringAsFixed(1)); + _changedHere = true; + eventBus.fire(new ServiceCallEvent(entity.domain, "set_temperature", entity.entityId,{"target_temp_high": "${_tmpTargetHigh.toStringAsFixed(1)}", "target_temp_low": "${_tmpTargetLow.toStringAsFixed(1)}"})); + _resetStateTimer(entity); + }); + } + void _setOperationMode(ClimateEntity entity, value) { setState(() { _tmpOperationMode = value; @@ -278,6 +321,24 @@ class _ClimateControlWidgetState extends State { }); } + void _setSwingMode(ClimateEntity entity, value) { + setState(() { + _tmpSwingMode = value; + _changedHere = true; + eventBus.fire(new ServiceCallEvent(entity.domain, "set_swing_mode", entity.entityId,{"swing_mode": "$_tmpSwingMode"})); + _resetStateTimer(entity); + }); + } + + void _setFanMode(ClimateEntity entity, value) { + setState(() { + _tmpFanMode = value; + _changedHere = true; + eventBus.fire(new ServiceCallEvent(entity.domain, "set_fan_mode", entity.entityId,{"fan_mode": "$_tmpFanMode"})); + _resetStateTimer(entity); + }); + } + void _setAwayMode(ClimateEntity entity, value) { setState(() { _tmpAwayMode = value; @@ -287,6 +348,24 @@ class _ClimateControlWidgetState extends State { }); } + void _setOnOf(ClimateEntity entity, value) { + setState(() { + _tmpIsOff = !value; + _changedHere = true; + eventBus.fire(new ServiceCallEvent(entity.domain, "${_tmpIsOff ? 'turn_off' : 'turn_on'}", entity.entityId, null)); + _resetStateTimer(entity); + }); + } + + void _setAuxHeat(ClimateEntity entity, value) { + setState(() { + _tmpAuxHeat = value; + _changedHere = true; + eventBus.fire(new ServiceCallEvent(entity.domain, "set_aux_heat", entity.entityId, {"aux_heat": "$_tmpAuxHeat"})); + _resetStateTimer(entity); + }); + } + void _resetStateTimer(ClimateEntity entity) { if (_resetTimer!=null) { _resetTimer.cancel(); @@ -313,36 +392,92 @@ class _ClimateControlWidgetState extends State { child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ - Text("Target temperature for ${_tmpOperationMode != 'off' ? _tmpOperationMode : ''}", style: TextStyle( - fontSize: entity.stateFontSize - )), - Row( - children: [ - Expanded( - child: Text( - "$_tmpTemperature", - style: TextStyle( - fontSize: entity.largeFontSize, - color: _showPending ? Colors.red : Colors.black - ), - ), + _buildOnOffControl(entity), + _buildTemperatureControls(entity), + _buildOperationControl(entity), + _buildFanControl(entity), + _buildSwingControl(entity), + _buildAwayModeControl(entity), + _buildAuxHeatControl(entity) + ], + ), + ); + } + + Widget _buildAwayModeControl(ClimateEntity entity) { + if (entity.supportAwayMode) { + return Row( + children: [ + Expanded( + child: Text( + "Away mode", + style: TextStyle( + fontSize: entity.stateFontSize ), - Column( - children: [ - IconButton( - icon: Icon(Icons.keyboard_arrow_up), - iconSize: 30.0, - onPressed: () => _temperatureUp(entity), - ), - IconButton( - icon: Icon(Icons.keyboard_arrow_down), - iconSize: 30.0, - onPressed: () => _temperatureDown(entity), - ) - ], - ) - ], + ), ), + Switch( + onChanged: (value) => _setAwayMode(entity, value), + value: _tmpAwayMode, + ) + ], + ); + } else { + return Container(height: 0.0, width: 0.0,); + } + } + + Widget _buildOnOffControl(ClimateEntity entity) { + if (entity.supportOnOff) { + return Row( + children: [ + Expanded( + child: Text( + "On / Off", + style: TextStyle( + fontSize: entity.stateFontSize + ), + ), + ), + Switch( + onChanged: (value) => _setOnOf(entity, value), + value: !_tmpIsOff, + ) + ], + ); + } else { + return Container(height: 0.0, width: 0.0,); + } + } + + Widget _buildAuxHeatControl(ClimateEntity entity) { + if (entity.supportAwayMode) { + return Row( + children: [ + Expanded( + child: Text( + "Aux heat", + style: TextStyle( + fontSize: entity.stateFontSize + ), + ), + ), + Switch( + onChanged: (value) => _setAuxHeat(entity, value), + value: _tmpAuxHeat, + ) + ], + ); + } else { + return Container(height: 0.0, width: 0.0,); + } + } + + Widget _buildOperationControl(ClimateEntity entity) { + if (entity.supportOperationMode) { + return Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ Text("Operation", style: TextStyle( fontSize: entity.stateFontSize )), @@ -361,27 +496,167 @@ class _ClimateControlWidgetState extends State { }).toList(), onChanged: (mode) => _setOperationMode(entity, mode), ), - Padding( - padding: EdgeInsets.only(top: entity.rowPadding), - child: Row( - children: [ - Expanded( - child: Text( - "Away mode", - style: TextStyle( - fontSize: entity.stateFontSize - ), - ), - ), - Switch( - onChanged: (value) => _setAwayMode(entity, value), - value: _tmpAwayMode, - ) - ], - ), - ) + Container(height: entity.rowPadding,) ], - ), + ); + } else { + return Container(height: 0.0, width: 0.0); + } + } + + Widget _buildFanControl(ClimateEntity entity) { + if (entity.supportFanMode) { + return Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Text("Fan mode", style: TextStyle( + fontSize: entity.stateFontSize + )), + DropdownButton( + value: "$_tmpFanMode", + iconSize: 30.0, + style: TextStyle( + fontSize: entity.largeFontSize, + color: Colors.black, + ), + items: entity.fanList.map((String value) { + return new DropdownMenuItem( + value: value, + child: new Text(value), + ); + }).toList(), + onChanged: (mode) => _setFanMode(entity, mode), + ), + Container(height: entity.rowPadding,) + ], + ); + } else { + return Container(height: 0.0, width: 0.0); + } + } + + Widget _buildSwingControl(ClimateEntity entity) { + if (entity.supportSwingMode) { + return Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Text("Swing mode", style: TextStyle( + fontSize: entity.stateFontSize + )), + DropdownButton( + value: "$_tmpSwingMode", + iconSize: 30.0, + style: TextStyle( + fontSize: entity.largeFontSize, + color: Colors.black, + ), + items: entity.swingList.map((String value) { + return new DropdownMenuItem( + value: value, + child: new Text(value), + ); + }).toList(), + onChanged: (mode) => _setSwingMode(entity, mode), + ), + Container(height: entity.rowPadding,) + ], + ); + } else { + return Container(height: 0.0, width: 0.0); + } + } + + Widget _buildTemperatureControls(ClimateEntity entity) { + List result = []; + if (entity.supportTargetTemperature) { + result.addAll([ + Expanded( + child: Text( + "$_tmpTemperature", + style: TextStyle( + fontSize: entity.largeFontSize, + color: _showPending ? Colors.red : Colors.black + ), + ), + ), + Column( + children: [ + IconButton( + icon: Icon(Icons.keyboard_arrow_up), + iconSize: 30.0, + onPressed: () => _temperatureUp(entity), + ), + IconButton( + icon: Icon(Icons.keyboard_arrow_down), + iconSize: 30.0, + onPressed: () => _temperatureDown(entity), + ) + ], + ) + ]); + } else if (entity.supportTargetTemperatureHigh && entity.supportTargetTemperatureLow) { + result.addAll([ + Expanded( + child: Text( + "$_tmpTargetLow", + style: TextStyle( + fontSize: entity.largeFontSize, + color: _showPending ? Colors.red : Colors.black + ), + ), + ), + Column( + children: [ + IconButton( + icon: Icon(Icons.keyboard_arrow_up), + iconSize: 30.0, + onPressed: () => _targetLowUp(entity), + ), + IconButton( + icon: Icon(Icons.keyboard_arrow_down), + iconSize: 30.0, + onPressed: () => _targetLowDown(entity), + ) + ], + ), + Container(width: 20.0,), + Expanded( + child: Text( + "$_tmpTargetHigh", + style: TextStyle( + fontSize: entity.largeFontSize, + color: _showPending ? Colors.red : Colors.black + ), + ), + ), + Column( + children: [ + IconButton( + icon: Icon(Icons.keyboard_arrow_up), + iconSize: 30.0, + onPressed: () => _targetHighUp(entity), + ), + IconButton( + icon: Icon(Icons.keyboard_arrow_down), + iconSize: 30.0, + onPressed: () => _targetHighDown(entity), + ) + ], + ) + ]); + } else { + result.add(Text("Unsupported temperature controls =(")); + } + return Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Text("Target temperature", style: TextStyle( + fontSize: entity.stateFontSize + )), + Row( + children: result, + ) + ], ); } diff --git a/lib/entity_class/stateless_widgets.dart b/lib/entity_class/stateless_widgets.dart index 11f5472..266fc50 100644 --- a/lib/entity_class/stateless_widgets.dart +++ b/lib/entity_class/stateless_widgets.dart @@ -350,7 +350,7 @@ class ClimateStateWidget extends StatelessWidget { fontSize: entityModel.entity.stateFontSize, )), Text( - entity.temperature!= null ? " ${entity.temperature}" : " ${entity.targetLow} - ${entity.targetHigh}", + entity.supportTargetTemperature ? " ${entity.temperature}" : " ${entity.targetLow} - ${entity.targetHigh}", textAlign: TextAlign.right, style: new TextStyle( fontSize: entityModel.entity.stateFontSize, diff --git a/lib/home_assistant.class.dart b/lib/home_assistant.class.dart index b38e76c..33a5f97 100644 --- a/lib/home_assistant.class.dart +++ b/lib/home_assistant.class.dart @@ -192,7 +192,7 @@ class HomeAssistant { _handleMessage(String message) { var data = json.decode(message); - TheLogger.log("Debug","[Received] => $data"); + TheLogger.log("Debug","[Received] => ${data['type']}"); if (data["type"] == "auth_required") { _sendAuthMessageRaw('{"type": "auth","$_authType": "$_password"}'); } else if (data["type"] == "auth_ok") {