-
Notifications
You must be signed in to change notification settings - Fork 217
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
New plugin: Tasmota Smart Plug control v1.0; based on & currently
basically identical functionality as the Edimax plugin (except added support for multi socket strips)
- Loading branch information
Showing
6 changed files
with
642 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1 @@ | ||
../plugins/tasmotasp |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,89 @@ | ||
========================== | ||
Tasmota Smart Plug Control | ||
========================== | ||
|
||
**Smart Plug control for hardware running the Tasmota open source firmware** | ||
|
||
Version 1.0 by Michael Balzer <dexter@dexters-web.de> | ||
|
||
- `Tasmota open source firmware <https://tasmota.github.io/>`_ | ||
- `List of Tasmota based smart plugs <https://templates.blakadder.com/plug.html>`_ | ||
|
||
The plugin primarily aims at using the smart plug to control & automate starting | ||
and/or stopping of the charge process, but of course can also be used to implement | ||
remote power control for other devices/processes. | ||
|
||
The smart plug can be bound to a defined location. Automatic periodic recharging or charge stop can | ||
be configured via main battery SOC level and/or 12V battery voltage level. The plugin can also | ||
switch off power at the charge stop event of the main and/or 12V battery. | ||
|
||
------------ | ||
Installation | ||
------------ | ||
|
||
1. Save :download:`tasmotasp.js` as ``/store/scripts/lib/tasmotasp.js`` | ||
2. Add line to ``/store/scripts/ovmsmain.js``: | ||
|
||
- ``tasmotasp = require("lib/tasmotasp");`` | ||
|
||
3. Issue ``script reload`` | ||
4. Install :download:`tasmotasp.htm` web plugin, recommended setup: | ||
|
||
- Type: Page | ||
- Page: ``/usr/tasmotasp`` | ||
- Label: Tasmota Smart Plug | ||
- Menu: Config | ||
- Auth: Cookie | ||
|
||
5. Install :download:`tasmotasp-status.htm` web plugin, recommended setup: | ||
|
||
- Type: Hook | ||
- Page: ``/status`` | ||
- Hook: ``body.post`` | ||
|
||
The ``tasmotasp-status.htm`` plugin adds power control to the Status page's vehicle panel. | ||
|
||
------------- | ||
Configuration | ||
------------- | ||
|
||
Use the web frontend for simple configuration. | ||
|
||
.. code-block:: none | ||
Param Instance Description | ||
usr tasmotasp.ip Tasmota IP address | ||
usr tasmotasp.user optional: username | ||
usr tasmotasp.pass optional: password | ||
usr tasmotasp.socket optional: output id (1-8, 0=all, empty=default) | ||
usr tasmotasp.location optional: restrict auto switch to this location | ||
usr tasmotasp.soc_on optional: switch on if SOC at/below | ||
usr tasmotasp.soc_off optional: switch off if SOC at/above | ||
usr tasmotasp.chg_stop_off optional: yes = switch off on vehicle.charge.stop | ||
usr tasmotasp.aux_volt_on optional: switch on if 12V level at/below | ||
usr tasmotasp.aux_volt_off optional: switch off if 12V level at/above | ||
usr tasmotasp.aux_stop_off optional: yes = switch off on vehicle.charge.12v.stop | ||
----- | ||
Usage | ||
----- | ||
|
||
.. code-block:: none | ||
script eval tasmotasp.get() | ||
script eval tasmotasp.set("on" | "off" | 0 | 1 | true | false) | ||
script eval tasmotasp.info() | ||
Note: ``get()`` & ``set()`` do an async update (if the location matches), the result is logged. | ||
Use ``info()`` to show the current state. | ||
|
||
------ | ||
Events | ||
------ | ||
|
||
.. code-block:: none | ||
usr.tasmotasp.on | ||
usr.tasmotasp.off | ||
usr.tasmotasp.error | ||
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,72 @@ | ||
<!-- | ||
Web UI plugin for page /status hook body.post: | ||
Add Tasmota Smart Plug power control button to Vehicle panel | ||
Tasmota Smart Plug plugin Version 1.0 Michael Balzer <dexter@dexters-web.de> | ||
--> | ||
|
||
<script> | ||
(function(){ | ||
|
||
var tasmotasp = { cfg: {}, state: { power: "", error: "" } }; | ||
var $receiver = $('#livestatus'); | ||
var $actionres = $('#vehicle-cmdres'); | ||
var $actionset; | ||
|
||
// State & UI update: | ||
function update(data) { | ||
$.extend(true, tasmotasp, data); | ||
// update state buttons: | ||
$actionres.empty(); | ||
$actionset.removeClass('active'); | ||
if (tasmotasp.state.power) { | ||
$actionset.find('input[value='+tasmotasp.state.power+']') | ||
.prop('checked', true) | ||
.parent().addClass('active'); | ||
} | ||
// show error: | ||
if (tasmotasp.state.error) { | ||
$actionres.text("TasmotaSP: " + tasmotasp.state.error); | ||
} | ||
} | ||
|
||
// Listen to tasmotasp events: | ||
$receiver.on('msg:event', function(e, event) { | ||
if (event == "usr.tasmotasp.on") | ||
update({ state: { power: "on" } }); | ||
else if (event == "usr.tasmotasp.off") | ||
update({ state: { power: "off" } }); | ||
else if (event == "usr.tasmotasp.error" || event == "config.changed") | ||
getInfo(); | ||
}); | ||
|
||
// Get state & config: | ||
function getInfo() { | ||
loadjs('tasmotasp.info()').then(function(output) { | ||
update(JSON.parse(output)); | ||
}); | ||
} | ||
|
||
// Init: | ||
$('#main').one('load', function(ev) { | ||
// add buttons to panel: | ||
$('#vehicle-cmdres').parent().before( | ||
'<li>'+ | ||
'<label class="control-label">Power:</label>'+ | ||
'<div class="btn-group action-tasmotasp-set" data-toggle="buttons">'+ | ||
'<label class="btn btn-default btn-sm action-tasmotasp-off"><input type="radio" name="power" value="off">OFF</label>'+ | ||
'<label class="btn btn-default btn-sm action-tasmotasp-on"><input type="radio" name="power" value="on">ON</label>'+ | ||
'</div>'+ | ||
'</li>'); | ||
$actionset = $('.action-tasmotasp-set > label'); | ||
// add button handler: | ||
$('.action-tasmotasp-set input').on('change', function(e) { | ||
$actionres.empty(); | ||
tasmotasp.state.power = $(this).val(); | ||
loadjs('tasmotasp.set("'+tasmotasp.state.power+'")', '#action-tasmotasp-output'); | ||
}); | ||
// get status: | ||
getInfo(); | ||
}); | ||
|
||
})(); | ||
</script> |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,232 @@ | ||
<!-- | ||
Web UI page plugin: | ||
Control & configuration frontend for Tasmota Smart Plug module plugin (tasmotasp.js) | ||
Tasmota Smart Plug Plugin Version 1.0 Michael Balzer <dexter@dexters-web.de> | ||
Recommended installation: | ||
- Page: /usr/tasmotasp | ||
- Label: Tasmota Smart Plug | ||
- Menu: Config | ||
- Auth: Cookie | ||
--> | ||
|
||
<div class="panel panel-primary panel-single"> | ||
<div class="panel-heading">Tasmota Smart Plug</div> | ||
<div class="panel-body"> | ||
<div class="alert alert-dismissible" style="display:none" /> | ||
<div class="receiver" id="tasmotasp"> | ||
|
||
<form class="form-horizontal" id="form-state"> | ||
<fieldset><legend>State</legend> | ||
<div class="form-group"> | ||
<label class="control-label col-sm-3">Power:</label> | ||
<div class="col-sm-9"> | ||
<div class="btn-group action-tasmotasp-set" data-toggle="buttons"> | ||
<label class="btn btn-default action-tasmotasp-off"><input type="radio" name="power" value="off">OFF</label> | ||
<label class="btn btn-default action-tasmotasp-on"><input type="radio" name="power" value="on">ON</label> | ||
</div> | ||
<samp class="samp-inline" id="action-tasmotasp-output"></samp> | ||
</div> | ||
</div> | ||
</fieldset> | ||
</form> | ||
|
||
<form class="form-horizontal" id="form-cfg"> | ||
|
||
<fieldset><legend>Configuration</legend> | ||
<div class="form-group"> | ||
<label class="control-label col-sm-3" for="input-ip">Tasmota IP address:</label> | ||
<div class="col-sm-9"> | ||
<input type="text" class="form-control font-monospace" name="ip" id="input-ip"> | ||
</div> | ||
</div> | ||
<div class="form-group"> | ||
<label class="control-label col-sm-3" for="input-user">… Username:</label> | ||
<div class="col-sm-9"> | ||
<input type="text" class="form-control font-monospace" name="user" id="input-user" | ||
autocomplete="section-tasmotasp username"> | ||
</div> | ||
</div> | ||
<div class="form-group"> | ||
<label class="control-label col-sm-3" for="input-pass">… Password:</label> | ||
<div class="col-sm-9"> | ||
<input type="password" class="form-control font-monospace" name="pass" id="input-pass" | ||
autocomplete="section-tasmotasp current-password"> | ||
</div> | ||
</div> | ||
<div class="form-group"> | ||
<label class="control-label col-sm-3" for="input-socket">… Socket:</label> | ||
<div class="col-sm-9"> | ||
<input type="text" class="form-control font-monospace" name="socket" id="input-socket" | ||
placeholder="optional output id (1-8, 0=all, empty=default)"> | ||
<span class="help-block"> | ||
<p>For multiple socket strips: enter the output number (1-8) to control. | ||
Id 0 controls all outputs simultaneously. Leave empty to use the device | ||
default output.</p> | ||
</span> | ||
</div> | ||
</div> | ||
<div class="form-group"> | ||
<label class="control-label col-sm-3" for="input-location">Location:</label> | ||
<div class="col-sm-9"> | ||
<input type="text" class="form-control" name="location" id="input-location" | ||
placeholder="optional: defined location name"> | ||
</div> | ||
</div> | ||
</fieldset> | ||
|
||
<fieldset><legend>Main Battery Guard</legend> | ||
<div class="form-group"> | ||
<label class="control-label col-sm-3" for="input-soc_on">Power on at:</label> | ||
<div class="col-sm-9"> | ||
<div class="form-control slider" id="soc_on" data-min="0" data-max="100" data-step="1" data-unit="% SOC" /> | ||
</div> | ||
</div> | ||
<div class="form-group"> | ||
<label class="control-label col-sm-3" for="input-soc_off">Power off at:</label> | ||
<div class="col-sm-9"> | ||
<div class="form-control slider" id="soc_off" data-min="0" data-max="100" data-step="1" data-unit="% SOC" /> | ||
</div> | ||
</div> | ||
<div class="form-group"> | ||
<div class="col-sm-9 col-sm-offset-3"> | ||
<div class="checkbox"> | ||
<label><input type="checkbox" name="chg_stop_off" id="input-chg_stop_off" value="yes">Power off at charge stop</label> | ||
</div> | ||
</div> | ||
</div> | ||
</fieldset> | ||
|
||
<fieldset><legend>12V Battery Guard</legend> | ||
<div class="form-group"> | ||
<label class="control-label col-sm-3" for="input-aux_volt_on">Power on at:</label> | ||
<div class="col-sm-9"> | ||
<div class="form-control slider" id="aux_volt_on" data-min="10" data-max="15" data-step="0.1" data-unit="V" /> | ||
</div> | ||
</div> | ||
<div class="form-group"> | ||
<label class="control-label col-sm-3" for="input-aux_volt_off">Power off at:</label> | ||
<div class="col-sm-9"> | ||
<div class="form-control slider" id="aux_volt_off" data-min="10" data-max="15" data-step="0.1" data-unit="V" /> | ||
</div> | ||
</div> | ||
<div class="form-group"> | ||
<div class="col-sm-9 col-sm-offset-3"> | ||
<div class="checkbox"> | ||
<label><input type="checkbox" name="aux_stop_off" id="input-aux_stop_off" value="yes">Power off at charge stop</label> | ||
</div> | ||
</div> | ||
</div> | ||
</fieldset> | ||
|
||
<div class="form-group"> | ||
<div class="col-sm-offset-3 col-sm-9"> | ||
<button type="button" class="btn btn-primary action-save">Save</button> | ||
</div> | ||
</div> | ||
|
||
</form> | ||
|
||
</div> | ||
</div> | ||
</div> | ||
|
||
<script> | ||
(function(){ | ||
|
||
var tasmotasp = { cfg: {}, state: { power: "", error: "" } }; | ||
var $actionset = $('#tasmotasp .action-tasmotasp-set > label'); | ||
var $actionres = $('#action-tasmotasp-output'); | ||
var $alert = $('.alert'); | ||
|
||
// Alert utility: | ||
function showAlert(type, text) { | ||
var intro = { | ||
"danger": "<strong>⚠</strong> ", | ||
"success": "<strong>✓</strong> ", | ||
}; | ||
$alert.attr('class', 'alert alert-' + type) | ||
.html('<p class="lead">' + intro[type] + encode_html(text) + '</p>') | ||
.show(); | ||
} | ||
|
||
// State & UI update: | ||
function update(data) { | ||
$.extend(true, tasmotasp, data); | ||
// update state form: | ||
$actionres.empty(); | ||
$actionset.removeClass('active'); | ||
if (tasmotasp.state.power) { | ||
$actionset.find('input[value='+tasmotasp.state.power+']') | ||
.prop('checked', true) | ||
.parent().addClass('active'); | ||
} | ||
// update config form: | ||
$('#input-ip').val(tasmotasp.cfg.ip); | ||
$('#input-user').val(tasmotasp.cfg.user); | ||
$('#input-pass').val(tasmotasp.cfg.pass); | ||
$('#input-socket').val(tasmotasp.cfg.socket); | ||
$('#input-location').val(tasmotasp.cfg.location); | ||
$('#soc_on').slider({ checked: (tasmotasp.cfg.soc_on != ""), value: Number(tasmotasp.cfg.soc_on) }); | ||
$('#soc_off').slider({ checked: (tasmotasp.cfg.soc_off != ""), value: Number(tasmotasp.cfg.soc_off) }); | ||
$('#input-chg_stop_off').prop("checked", tasmotasp.cfg.chg_stop_off == "yes"); | ||
$('#aux_volt_on').slider({ checked: (tasmotasp.cfg.aux_volt_on != ""), value: Number(tasmotasp.cfg.aux_volt_on) }); | ||
$('#aux_volt_off').slider({ checked: (tasmotasp.cfg.aux_volt_off != ""), value: Number(tasmotasp.cfg.aux_volt_off) }); | ||
$('#input-aux_stop_off').prop("checked", tasmotasp.cfg.aux_stop_off == "yes"); | ||
// show error: | ||
if (tasmotasp.state.error) { | ||
showAlert("danger", tasmotasp.state.error); | ||
} | ||
} | ||
|
||
// Listen to tasmotasp events: | ||
$('#tasmotasp').on('msg:event', function(e, event) { | ||
if (event == "usr.tasmotasp.on") | ||
update({ state: { power: "on" } }); | ||
else if (event == "usr.tasmotasp.off") | ||
update({ state: { power: "off" } }); | ||
else if (event == "usr.tasmotasp.error" || event == "config.changed") | ||
getInfo(); | ||
}); | ||
|
||
// Get state & config: | ||
function getInfo() { | ||
loadcmd('script eval tasmotasp.info()').then(function(output) { | ||
update(JSON.parse(output)); | ||
}); | ||
} | ||
|
||
// Switch power: | ||
$('.action-tasmotasp-set input').on('change', function(e) { | ||
$alert.hide(); | ||
tasmotasp.state.power = $(this).val(); | ||
loadcmd('script eval tasmotasp.set("'+tasmotasp.state.power+'")', '#action-tasmotasp-output'); | ||
}); | ||
|
||
// Save config: | ||
$('.action-save').on('click', function(e) { | ||
$alert.hide(); | ||
$actionres.empty(); | ||
var upd = { | ||
soc_on: "", soc_off: "", chg_stop_off: "", | ||
aux_volt_on: "", aux_volt_off: "", aux_stop_off: "" | ||
}; | ||
var inp = $('#form-cfg').serializeArray(); | ||
inp.map(el => upd[el["name"]] = el["value"]); | ||
loadcmd('script eval \'OvmsConfig.SetValues("usr","tasmotasp.",'+JSON.stringify(upd)+')\'').then( | ||
function() { | ||
showAlert("success", "OK, configuration saved!"); | ||
}, | ||
function(req,status,error) { | ||
showAlert("danger", "Failed saving: " + status + "/" + error); | ||
}); | ||
}); | ||
|
||
// Init: | ||
$('#main').one('load', function(ev) { | ||
$('.slider').slider(); | ||
getInfo(); | ||
}); | ||
|
||
})(); | ||
</script> |
Oops, something went wrong.