diff --git a/lib/entity.class.dart b/lib/entity.class.dart index d4a10c2..eb6f89f 100644 --- a/lib/entity.class.dart +++ b/lib/entity.class.dart @@ -11,6 +11,14 @@ class Entity { String get domain => _domain; String get entityId => _entityId; String get state => _state; + set state(value) => _state = value; + + double get minValue => _attributes["min"] ?? 0.0; + double get maxValue => _attributes["max"] ?? 100.0; + double get valueStep => _attributes["step"] ?? 1.0; + double get doubleState => double.tryParse(_state) ?? 0.0; + bool get isSlider => _attributes["mode"] == "slider"; + String get deviceClass => _attributes["device_class"] ?? null; bool get isView => (_domain == "group") && (_attributes != null ? _attributes["view"] ?? false : false); bool get isGroup => _domain == "group"; @@ -24,6 +32,10 @@ class Entity { update(rawData); } + int getValueDivisions() { + return ((maxValue - minValue)/valueStep).round().round(); + } + void update(Map rawData) { _attributes = rawData["attributes"] ?? {}; _domain = rawData["entity_id"].split(".")[0]; diff --git a/lib/home_assistant.class.dart b/lib/home_assistant.class.dart index 38df25c..c48eba2 100644 --- a/lib/home_assistant.class.dart +++ b/lib/home_assistant.class.dart @@ -233,7 +233,7 @@ class HomeAssistant { _statesCompleter.complete(); } - Future callService(String domain, String service, String entity_id) { + Future callService(String domain, String service, String entityId, Map additionalParams) { var sendCompleter = Completer(); //TODO: Send service call timeout timer. Should be removed after #21 fix Timer _sendTimer = Timer(Duration(seconds: 7), () { @@ -241,7 +241,14 @@ class HomeAssistant { }); _reConnectSocket().then((r) { _incrementMessageId(); - _sendMessageRaw('{"id": $_currentMessageId, "type": "call_service", "domain": "$domain", "service": "$service", "service_data": {"entity_id": "$entity_id"}}'); + String message = '{"id": $_currentMessageId, "type": "call_service", "domain": "$domain", "service": "$service", "service_data": {"entity_id": "$entityId"'; + if (additionalParams != null) { + additionalParams.forEach((name, value){ + message += ', "$name" : "$value"'; + }); + } + message += '}}'; + _sendMessageRaw(message); _sendTimer.cancel(); sendCompleter.complete(); }).catchError((e){ diff --git a/lib/main.dart b/lib/main.dart index 9680479..a3a87c2 100644 --- a/lib/main.dart +++ b/lib/main.dart @@ -178,11 +178,11 @@ class _MainPageState extends State with WidgetsBindingObserver { }); } - void _callService(String domain, String service, String entityId) { + void _callService(String domain, String service, String entityId, Map additionalParams) { setState(() { _isLoading = true; }); - _homeAssistant.callService(domain, service, entityId).then((r) { + _homeAssistant.callService(domain, service, entityId, additionalParams).then((r) { setState(() { _isLoading = false; }); @@ -386,7 +386,33 @@ class _MainPageState extends State with WidgetsBindingObserver { ids.forEach((id) { var data = _entities.get(id); if (data != null) { - entities.add(new ListTile( + entities.add( + Padding( + padding: EdgeInsets.fromLTRB(0.0, 10.0, 0.0, 10.0), + child: SizedBox( + height: 34.0, + child: Row( + children: [ + Padding( + padding: EdgeInsets.fromLTRB(8.0, 0.0, 12.0, 0.0), + child: MaterialDesignIcons.createIconWidgetFromEntityData(data, 28.0, _stateIconColors[data.state] ?? Colors.blueGrey), + ), + Expanded( + child: Text( + "${data.displayName}", + overflow: TextOverflow.fade, + softWrap: false, + style: TextStyle( + fontSize: 16.0 + ), + ), + ), + _buildEntityActionWidget(data) + ], + ), + ), + ) + /*new ListTile( leading: MaterialDesignIcons.createIconWidgetFromEntityData(data, 28.0, _stateIconColors[data.state] ?? Colors.blueGrey), //subtitle: Text("${data['entity_id']}"), trailing: _buildEntityActionWidget(data), @@ -395,7 +421,7 @@ class _MainPageState extends State with WidgetsBindingObserver { overflow: TextOverflow.fade, softWrap: false, ), - )); + )*/); } }); return entities; @@ -412,7 +438,7 @@ class _MainPageState extends State with WidgetsBindingObserver { value: entity.isOn, onChanged: ((state) { _callService( - entity.domain, state ? "turn_on" : "turn_off", entityId + entity.domain, state ? "turn_on" : "turn_off", entityId, null ); //TODO remove after checking if state will chenge without setState but after socket event /*setState(() { @@ -425,25 +451,64 @@ class _MainPageState extends State with WidgetsBindingObserver { case "script": case "scene": { - result = SizedBox( - width: 60.0, - child: FlatButton( - onPressed: (() { - _callService(entity.domain, "turn_on", entityId); - }), - child: Text( - "Run", - textAlign: TextAlign.right, - style: new TextStyle(fontSize: 16.0, color: Colors.blue), - ), - ) + result = FlatButton( + onPressed: (() { + _callService(entity.domain, "turn_on", entityId, null); + }), + child: Text( + "EXECUTE", + textAlign: TextAlign.right, + style: new TextStyle(fontSize: 16.0, color: Colors.blue), + ), ); break; } + case "input_number": { + if (entity.isSlider) { + result = Container( + width: 200.0, + child: Row( + children: [ + Expanded( + child: Slider( + min: entity.minValue*10, + max: entity.maxValue*10, + value: entity.doubleState*10, + divisions: entity.getValueDivisions(), + onChanged: (value) { + setState(() { + entity.state = (value.roundToDouble() / 10).toString(); + }); + }, + onChangeEnd: (value) { + _callService(entity.domain, "set_value", entityId, + {"value": "${entity.state}"}); + }, + ), + ), + Padding( + padding: EdgeInsets.only(right: 16.0), + child: Text( + "${entity.state}${entity.unitOfMeasurement}", + textAlign: TextAlign.right, + style: new TextStyle( + fontSize: 16.0, + ) + ), + ) + ], + ), + ); + } else { + //TODO draw box instead of slider + } + break; + } + default: { result = Padding( - padding: EdgeInsets.fromLTRB(0.0, 0.0, 16.0, 0.0), + padding: EdgeInsets.fromLTRB(0.0, 0.0, 14.0, 0.0), child: Text( "${entity.state}${entity.unitOfMeasurement}", textAlign: TextAlign.right,