Skip to content

Commit

Permalink
Battery operated devices support
Browse files Browse the repository at this point in the history
* "Battery operated" setting added to Simple Devices
* Charts small fix
* Thumbnail script small fix
  • Loading branch information
sergejey committed Jul 31, 2022
1 parent c772b18 commit 205e863
Show file tree
Hide file tree
Showing 28 changed files with 155 additions and 43 deletions.
4 changes: 4 additions & 0 deletions languages/default.php
Original file line number Diff line number Diff line change
Expand Up @@ -1195,6 +1195,10 @@
'DEVICES_TURNOFF_LIGHTS_ON_IDLE' => 'Turn off lights in the room on idle',
'DEVICES_ALIVE_TIMEOUT' => 'Possible inactivity timeout (hours)',
'DEVICES_MAIN_SENSOR' => 'Main sensor for the room',
'DEVICES_BATTERY_OPERATED' => 'Device is battery operated',
'DEVICES_BATTERY_LEVEL' => 'Battery level',
'DEVICES_BATTERY_WARNING' => 'Low battery warning',
'DEVICES_LOW_BATTERY' => 'low battery level',

'DEVICES_IS_ON' => 'is ON',
'DEVICES_IS_CLOSED' => 'is Closed',
Expand Down
4 changes: 4 additions & 0 deletions languages/ru.php
Original file line number Diff line number Diff line change
Expand Up @@ -1192,6 +1192,10 @@
'DEVICES_ALIVE_TIMEOUT' => 'Допустимое время отсутствия данных (часов)',
'DEVICES_MAIN_SENSOR' => 'Основной сенсор помещения',
'DEVICES_NOT_UPDATING' => 'не обновляется',
'DEVICES_BATTERY_OPERATED' => 'Устройство работает от батареи',
'DEVICES_BATTERY_LEVEL' => 'Уровень заряда батареи',
'DEVICES_BATTERY_WARNING' => 'Предупреждение о низком заряде батареи',
'DEVICES_LOW_BATTERY' => 'низкий уровень заряда',

'DEVICES_IS_ON' => 'Включено',
'DEVICES_IS_CLOSED' => 'Закрыто',
Expand Down
1 change: 1 addition & 0 deletions lib/objects.class.php
Original file line number Diff line number Diff line change
Expand Up @@ -57,6 +57,7 @@ function getClassTemplate($class_id,$view='')
if (!$class['ID']) {
return '';
}

if ($view!='' && file_exists(DIR_TEMPLATES . 'classes/views/' . $class['TITLE'] . '_'.$view.'.html')) {
$class_file_path = DIR_TEMPLATES . 'classes/views/' . $class['TITLE'] . '_'.$view.'.html';
$alt_class_file_path = ROOT . 'templates_alt/classes/views/' . $class['TITLE'] . '_'.$view.'.html';
Expand Down
7 changes: 3 additions & 4 deletions lib/utils/postprocess_result.inc.php
Original file line number Diff line number Diff line change
Expand Up @@ -41,8 +41,7 @@

if (preg_match_all('/%(\w{2,}?)\.(\w{2,}?)\|(\d+)%/isu', $result, $m))
{

if (!defined('DISABLE_WEBSOCKETS') || DISABLE_WEBSOCKETS==0)
if (!defined('DISABLE_WEBSOCKETS') || DISABLE_WEBSOCKETS==0)
{
$tracked_properties=array();
$total = count($m[0]);
Expand All @@ -67,6 +66,7 @@
payload.data = new Object();
payload.data.TYPE='properties';
payload.data.PROPERTIES='".implode(',', $tracked_properties)."';
console.log('Subscription to properties sent.');
wsSocket.send(JSON.stringify(payload));
});\n";
$js.="function processPropertiesUpdate(data) {
Expand All @@ -85,8 +85,7 @@
});";
$js.='</script>';


$result=str_replace('</body>', $js.'</body>', $result);
$result=str_replace('<body', $js.'<body', $result);

} else {

Expand Down
7 changes: 5 additions & 2 deletions modules/devices/devices.class.php
Original file line number Diff line number Diff line change
Expand Up @@ -659,11 +659,14 @@ function manage_schedule(&$out)
*/
function usual(&$out)
{

$view = gr('view');
if ($view) $this->view=$view;

if ($this->ajax) {
header("HTTP/1.0: 200 OK\n");
header('Content-Type: text/html; charset=utf-8');
$op = gr('op');
$view = gr('view');
$res = array();
if ($op == 'clicked') {
$object = gr('object');
Expand All @@ -686,7 +689,7 @@ function usual(&$out)
$res = array();
foreach ($tmp as $id) {
if (!$id) continue;
$record = $this->processDevice($id);
$record = $this->processDevice($id,$view);
if (!$record['DEVICE_ID']) continue;
$res['devices'][] = $record;
}
Expand Down
36 changes: 32 additions & 4 deletions modules/devices/devices_search.inc.php
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,34 @@
$qry .= " AND devices.ARCHIVED=1";
} elseif ($group_name == 'is:system') {
$qry .= " AND devices.SYSTEM_DEVICE=1";
} elseif ($group_name == 'is:battery') {
$object_names = getObjectsByProperty('batteryOperated', 1);
if (!is_array($object_names)) {
$object_names = array(0);
}
$total = count($object_names);
if ($total > 0) {
for ($i = 0; $i < $total; $i++) {
$object_names[$i] = "'" . $object_names[$i] . "'";
}
$qry .= " AND devices.LINKED_OBJECT IN (" . implode(',', $object_names) . ")";
} else {
$qry .= " AND 0";
}
} elseif ($group_name == 'is:battery_low') {
$object_names = getObjectsByProperty('batteryWarning', 1);
if (!is_array($object_names)) {
$object_names = array(0);
}
$total = count($object_names);
if ($total > 0) {
for ($i = 0; $i < $total; $i++) {
$object_names[$i] = "'" . $object_names[$i] . "'";
}
$qry .= " AND devices.LINKED_OBJECT IN (" . implode(',', $object_names) . ")";
} else {
$qry .= " AND 0";
}
} elseif ($group_name) {
$object_names = getObjectsByProperty('group' . $group_name, 1);
if (!is_array($object_names)) {
Expand Down Expand Up @@ -60,7 +88,7 @@
}
if (!$qry) $qry = "1";

$tmp = SQLSelectOne("SELECT COUNT(*) AS TOTAL FROM devices");
$tmp = SQLSelectOne("SELECT COUNT(*) AS TOTAL FROM devices WHERE devices.ARCHIVED!=1");
$out['TOTAL'] = (int)$tmp['TOTAL'];

$loc_title = '';
Expand All @@ -71,15 +99,15 @@
if ($res[0]['ID']) {
//paging($res, 100, $out); // search result paging
$total = count($res);
$out['TOTAL_FOUND']=$total;
for ($i = 0; $i < $total; $i++) {
// some action for every record if required
if ($res[$i]['LOCATION_TITLE'] != $loc_title) {
$res[$i]['NEW_LOCATION'] = 1;
$loc_title = $res[$i]['LOCATION_TITLE'];
}
if ($res[$i]['LINKED_OBJECT']) {
if ($res[$i]['TYPE'] == 'camera') {
//$processed = $this->processDevice($res[$i]['ID'],'mini');
if ($res[$i]['TYPE'] == 'camera' || $res[$i]['TYPE'] == 'mark') {
$processed = $this->processDevice($res[$i]['ID'], 'list');
} else {
$processed = $this->processDevice($res[$i]['ID']);
Expand Down Expand Up @@ -119,7 +147,7 @@
foreach ($this->device_types as $k => $v) {
if ($v['TITLE']) {
$type_rec = array('NAME' => $k, 'TITLE' => $v['TITLE']);
$tmp = SQLSelectOne("SELECT COUNT(*) AS TOTAL FROM devices WHERE TYPE='" . $k . "'");
$tmp = SQLSelectOne("SELECT COUNT(*) AS TOTAL FROM devices WHERE TYPE='" . $k . "' AND ARCHIVED!=1");
$type_rec['TOTAL'] = (int)$tmp['TOTAL'];
if ($type_rec['TOTAL'] > 0) {
$types[] = $type_rec;
Expand Down
4 changes: 4 additions & 0 deletions modules/devices/devices_structure.inc.php
Original file line number Diff line number Diff line change
Expand Up @@ -26,11 +26,15 @@
'aliveTimeout'=>array('DESCRIPTION'=>LANG_DEVICES_ALIVE_TIMEOUT,'_CONFIG_TYPE'=>'num','_CONFIG_HELP'=>'SdAliveTimeout'),
'linkedRoom'=>array('DESCRIPTION'=>'LinkedRoom'),
'updated'=>array('DESCRIPTION'=>'Updated Timestamp'),
'batteryOperated'=>array('DESCRIPTION'=>LANG_DEVICES_BATTERY_OPERATED,'_CONFIG_TYPE'=>'yesno', 'ONCHANGE'=>'batteryLevelUpdated'),
'batteryLevel'=>array('DESCRIPTION'=>LANG_DEVICES_BATTERY_LEVEL, 'ONCHANGE'=>'batteryLevelUpdated'),
'batteryWarning'=>array('DESCRIPTION'=>LANG_DEVICES_BATTERY_WARNING),
),
'METHODS'=>array(
'statusUpdated'=>array('DESCRIPTION'=>'Status updated event'),
'logicAction'=>array('DESCRIPTION'=>'Logic Action'),
'keepAlive'=>array('DESCRIPTION'=>'Alive update'),
'batteryLevelUpdated'=>array('DESCRIPTION'=>'Battery level updated'),
),
'INJECTS'=>array(
'OperationalModes'=>array(
Expand Down
5 changes: 5 additions & 0 deletions modules/devices/system_checkstate.php
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,11 @@
$yellow_state=1;
$details[]=$devices[$idv]['TITLE'].' '.LANG_DEVICES_NOT_UPDATING;
}
$batteryWarning = gg($devices[$idv]['LINKED_OBJECT'] . '.batteryWarning');
if ($batteryWarning==1) {
$yellow_state=1;
$details[]=$devices[$idv]['TITLE'].' '.LANG_DEVICES_LOW_BATTERY;
}
}

/*
Expand Down
22 changes: 16 additions & 6 deletions modules/thumb/thumb.php
Original file line number Diff line number Diff line change
Expand Up @@ -144,27 +144,37 @@ function mjpeg_grab_frame($url)
}



if ($result) {

if ($live) {

$boundary = "my_mjpeg";
header('Accept-Range: bytes');
header("Cache-Control: no-cache");
header("Cache-Control: private");
header("Pragma: no-cache");
header("Content-type: multipart/x-mixed-replace; boundary=$boundary");
print "--$boundary\n";
set_time_limit(0);
@apache_setenv('no-gzip', 1);
if (function_exists('apache_setenv')) {
@apache_setenv('no-gzip', 1);
}
@ini_set('zlib.output_compression', 0);
@ini_set('implicit_flush', 1);
for ($i = 0; $i < ob_get_level(); $i++) ob_end_flush();
ob_implicit_flush(1);
ob_end_flush();
$counter=0;
print "Content-type: image/jpeg\n\n";
while (true) {
print "Content-type: image/jpeg\n\n";
$counter++;
$result = getURL($url, 0, $username, $password);
print $result;
print "--$boundary\n";
if ($result) {
$newimg = imagecreatefromstring($result);
imagejpeg($newimg);
print "--$boundary\n\n";
}
flush();
ob_flush();
sleep(1);
}

Expand Down
2 changes: 2 additions & 0 deletions pChart/index.php
Original file line number Diff line number Diff line change
Expand Up @@ -299,9 +299,11 @@
$code = '<img src="' . ROOTHTML . '3rdparty/jpgraph/?p=' . $p . '&type=' . $_GET['subop'] . '&width=500&"/>';
}
echo $code;
/*
if (!$_GET['minimal']) {
echo "<br/>" . htmlspecialchars($code);
}
*/
exit;
}
}
Expand Down
5 changes: 4 additions & 1 deletion templates/classes/views/SCO2Sensors.html
Original file line number Diff line number Diff line change
Expand Up @@ -3,5 +3,8 @@
<div class="device-header">%.value%
<span>%.direction|"-1=&#9660;1=&#9650;0=&#8801"%</span>
</div>
<div class="device-details">%.object_description%</div>
<div class="device-details">%.object_description%
<span style="display:%.batteryOperated|"none;inline"%;
%.batteryWarning|"0=color:green;1=color:red;2=color:lightsalmon"%;"><i class="glyphicon glyphicon-flash"></i>%.batteryLevel%%</span>
</div>
</div>
5 changes: 4 additions & 1 deletion templates/classes/views/SGeneralSensors.html
Original file line number Diff line number Diff line change
Expand Up @@ -3,5 +3,8 @@
<div class="device-header">%.value% %.unit%
<span>%.direction|"-1=&#9660;1=&#9650;0=&#8801"%</span>
</div>
<div class="device-details">%.object_description%</div>
<div class="device-details">%.object_description%
<span style="display:%.batteryOperated|"none;inline"%;
%.batteryWarning|"0=color:green;1=color:red;2=color:lightsalmon"%;"><i class="glyphicon glyphicon-flash"></i>%.batteryLevel%%</span>
</div>
</div>
5 changes: 4 additions & 1 deletion templates/classes/views/SHumSensors.html
Original file line number Diff line number Diff line change
Expand Up @@ -3,5 +3,8 @@
<div class="device-header">%.value%%
<span>%.direction|"-1=&#9660;1=&#9650;0=&#8801"%</span>
</div>
<div class="device-details">%.object_description%</div>
<div class="device-details">%.object_description%
<span style="display:%.batteryOperated|"none;inline"%;
%.batteryWarning|"0=color:green;1=color:red;2=color:lightsalmon"%;"><i class="glyphicon glyphicon-flash"></i>%.batteryLevel%%</span>
</div>
</div>
8 changes: 6 additions & 2 deletions templates/classes/views/SLeak.html
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,10 @@
<div class="device-widget %.alive|"offline;online"%">
<div class="device-icon leak %.status|"off;on"%"></div>
<div class="device-header">%.object_description%</div>
<!---<div class="device-details">%.updatedText%</div>--->
<div class="updatedText-JS-%.object_id%" style="font-size: 11px;">...</div>

<div class="device-details">
<span class="updatedText-JS-%.object_id%" style="font-size: 11px;">...</span>
<span style="display:%.batteryOperated|"none;inline"%;
%.batteryWarning|"0=color:green;1=color:red;2=color:lightsalmon"%;"><i class="glyphicon glyphicon-flash"></i>%.batteryLevel%%</span>
</div>
</div>
5 changes: 4 additions & 1 deletion templates/classes/views/SLightSensors.html
Original file line number Diff line number Diff line change
Expand Up @@ -3,5 +3,8 @@
<div class="device-header">%.value% %.unit% <sub>(min: %.periodMinValue% )</sub>
<span>%.direction|"-1=&#9660;1=&#9650;0=&#8801"%</span>
</div>
<div class="device-details">%.object_description%</div>
<div class="device-details">%.object_description%
<span style="display:%.batteryOperated|"none;inline"%;
%.batteryWarning|"0=color:green;1=color:red;2=color:lightsalmon"%;"><i class="glyphicon glyphicon-flash"></i>%.batteryLevel%%</span>
</div>
</div>
5 changes: 4 additions & 1 deletion templates/classes/views/SMoistureSensors.html
Original file line number Diff line number Diff line change
Expand Up @@ -3,5 +3,8 @@
<div class="device-header">%.value%%
<span>%.direction|"-1=&#9660;1=&#9650;0=&#8801"%</span>
</div>
<div class="device-details">%.object_description%</div>
<div class="device-details">%.object_description%
<span style="display:%.batteryOperated|"none;inline"%;
%.batteryWarning|"0=color:green;1=color:red;2=color:lightsalmon"%;"><i class="glyphicon glyphicon-flash"></i>%.batteryLevel%%</span>
</div>
</div>
11 changes: 7 additions & 4 deletions templates/classes/views/SMotions.html
Original file line number Diff line number Diff line change
Expand Up @@ -72,9 +72,9 @@
if(currentTime.getFullYear() == timeConvertVar[2] && twoDigits(currentTime.getMonth()+1) == timeConvertVar[1] && (currentTime.getDate() - timeConvertVar[0]) == 0) {
beautyText = '<#LANG_DEVICES_PASSED_TODAY_IN#> ' + timeConvertVar[3] + ':' + timeConvertVar[4];
} else if(currentTime.getFullYear() == timeConvertVar[2] && twoDigits(currentTime.getMonth()+1) == timeConvertVar[1] && (currentTime.getDate() - timeConvertVar[0]) == 1) {
beautyText = '<#LANG_DEVICES_PASSED_YESTERDAY_IN#> ' + timeConvertVar[3] + ':' + timeConvertVar[4] + ':' + timeConvertVar[5];
beautyText = '<#LANG_DEVICES_PASSED_YESTERDAY_IN#> ' + timeConvertVar[3] + ':' + timeConvertVar[4]; // + ':' + timeConvertVar[5]
} else {
beautyText = timeConvertVar[0] + '.' + timeConvertVar[1] + '.' + timeConvertVar[2] + ' ' + timeConvertVar[3] + ':' + timeConvertVar[4] + ':' + timeConvertVar[5];
beautyText = timeConvertVar[0] + '.' + timeConvertVar[1] + '.' + timeConvertVar[2] + ' ' + timeConvertVar[3] + ':' + timeConvertVar[4]; // + ':' + timeConvertVar[5]
}

$('.updatedText-JS-' + sID).text(beautyText);
Expand All @@ -97,6 +97,9 @@
<div class="device-widget motion %.alive|"offline;online"%" onclick="var url=('<#ROOTHTML#>pChart/?p=%.object_title%'+'.status&op=log&subop=24h&minimal=1');parent.$.fancybox.open({ src: url, type: 'iframe',iframe:{preload:false} });return false;">
<div class="device-icon motion %.status|"off;on"%"></div>
<div class="device-header">%.object_description%</div>
<!---<div class="device-details">%.updatedText%</div>--->
<div class="updatedText-JS-%.object_id%" style="font-size: 11px;">...</div>
<div class="device-details">
<span class="updatedText-JS-%.object_id%" style="font-size: 11px;">...</span>
<span style="display:%.batteryOperated|"none;inline"%;
%.batteryWarning|"0=color:green;1=color:red;2=color:lightsalmon"%;"><i class="glyphicon glyphicon-flash"></i>%.batteryLevel%%</span>
</div>
</div>
11 changes: 7 additions & 4 deletions templates/classes/views/SOpenClose.html
Original file line number Diff line number Diff line change
Expand Up @@ -72,9 +72,9 @@
if(twoDigits(currentTime.getMonth()+1) == timeConvertVar[1] && (currentTime.getDate() - timeConvertVar[0]) == 0) {
beautyText = '<#LANG_DEVICES_PASSED_TODAY_IN#> ' + timeConvertVar[3] + ':' + timeConvertVar[4];
} else if(twoDigits(currentTime.getMonth()+1) == timeConvertVar[1] && (currentTime.getDate() - timeConvertVar[0]) == 1) {
beautyText = '<#LANG_DEVICES_PASSED_YESTERDAY_IN#> ' + timeConvertVar[3] + ':' + timeConvertVar[4] + ':' + timeConvertVar[5];
beautyText = '<#LANG_DEVICES_PASSED_YESTERDAY_IN#> ' + timeConvertVar[3] + ':' + timeConvertVar[4]; // + ':' + timeConvertVar[5]
} else {
beautyText = timeConvertVar[0] + '.' + timeConvertVar[1] + '.' + timeConvertVar[2] + ' ' + timeConvertVar[3] + ':' + timeConvertVar[4] + ':' + timeConvertVar[5];
beautyText = timeConvertVar[0] + '.' + timeConvertVar[1] + '.' + timeConvertVar[2] + ' ' + timeConvertVar[3] + ':' + timeConvertVar[4]; // + ':' + timeConvertVar[5]
}

$('.openclose-updatedText-JS-' + sID).text(beautyText);
Expand All @@ -98,6 +98,9 @@
<div style='display:%.ncno|"nc=block;no=none"%'><div class="device-icon openclose %.status|"on;off"%"></div></div>
<div style='display:%.ncno|"nc=none;no=block;=none"%'><div class="device-icon openclose %.status|"off;on"%"></div></div>
<div class="device-header">%.object_description%</div>
<!---<div class="device-details">%.updatedText%</div>--->
<div class="openclose-updatedText-JS-%.object_id%" style="font-size: 11px;">...</div>
<div class="device-details">
<span class="openclose-updatedText-JS-%.object_id%" style="font-size: 11px;">...</span>
<span style="display:%.batteryOperated|"none;inline"%;
%.batteryWarning|"0=color:green;1=color:red;2=color:lightsalmon"%;"><i class="glyphicon glyphicon-flash"></i>%.batteryLevel%%</span>
</div>
</div>
10 changes: 7 additions & 3 deletions templates/classes/views/SOpenable.html
Original file line number Diff line number Diff line change
Expand Up @@ -9,9 +9,9 @@
if((currentTime.getDate() - timeConvertVar[0]) == 0) {
beautyText_%.object_id% = '<#LANG_DEVICES_PASSED_TODAY_IN#> ' + timeConvertVar[3] + ':' + timeConvertVar[4];
} else if((currentTime.getDate() - timeConvertVar[0]) == 1) {
beautyText_%.object_id% = '<#LANG_DEVICES_PASSED_YESTERDAY_IN#> ' + timeConvertVar[3] + ':' + timeConvertVar[4] + ':' + timeConvertVar[5];
beautyText_%.object_id% = '<#LANG_DEVICES_PASSED_YESTERDAY_IN#> ' + timeConvertVar[3] + ':' + timeConvertVar[4]; // + ':' + timeConvertVar[5]
} else {
beautyText_%.object_id% = timeConvertVar[0] + '.' + timeConvertVar[1] + '.' + timeConvertVar[2] + ' ' + timeConvertVar[3] + ':' + timeConvertVar[4] + ':' + timeConvertVar[5];
beautyText_%.object_id% = timeConvertVar[0] + '.' + timeConvertVar[1] + '.' + timeConvertVar[2] + ' ' + timeConvertVar[3] + ':' + timeConvertVar[4]; // + ':' + timeConvertVar[5]
}
}
$('.updatedText-JS-' + sID_%.object_id%).text(beautyText_%.object_id%);
Expand Down Expand Up @@ -58,6 +58,10 @@
<option value="100">100%</option>
</select>
</div>
<div class="updatedText-JS-%.object_id%" style="font-size: 11px;">...</div>
<div class="device-details">
<span class="updatedText-JS-%.object_id%" style="font-size: 11px;">...</span>
<span style="display:%.batteryOperated|"none;inline"%;
%.batteryWarning|"0=color:green;1=color:red;2=color:lightsalmon"%;"><i class="glyphicon glyphicon-flash"></i>%.batteryLevel%%</span>
</div>
</div>
<!--support_level-->
5 changes: 4 additions & 1 deletion templates/classes/views/SPercentageSensors.html
Original file line number Diff line number Diff line change
@@ -1,5 +1,8 @@
<div class="device-widget sensor %.alive|"offline;online"%" onclick="var url=('<#ROOTHTML#>pChart/?p=%.object_title%'+'.value&op=log&subop=24h&minimal=1');parent.$.fancybox.open({ src: url, type: 'iframe',iframe:{preload:false} });return false;">
<div class="device-icon"></div>
<div class="device-header">%.value%%</div>
<div class="device-details">%.object_description%</div>
<div class="device-details">%.object_description%
<span style="display:%.batteryOperated|"none;inline"%;
%.batteryWarning|"0=color:green;1=color:red;2=color:lightsalmon"%;"><i class="glyphicon glyphicon-flash"></i>%.batteryLevel%%</span>
</div>
</div>
5 changes: 4 additions & 1 deletion templates/classes/views/SPressureSensors.html
Original file line number Diff line number Diff line change
@@ -1,5 +1,8 @@
<div class="device-widget sensor %.alive|"offline;online"%" onclick="var url=('<#ROOTHTML#>pChart/?p=%.object_title%'+'.value&op=log&subop=24h&minimal=1');parent.$.fancybox.open({ src: url, type: 'iframe',iframe:{preload:false} });return false;">
<div class="device-icon pressure"></div>
<div class="device-header">%.value% <#LANG_M_PRESSURE#></div>
<div class="device-details">%.object_description%</div>
<div class="device-details">%.object_description%
<span style="display:%.batteryOperated|"none;inline"%;
%.batteryWarning|"0=color:green;1=color:red;2=color:lightsalmon"%;"><i class="glyphicon glyphicon-flash"></i>%.batteryLevel%%</span>
</div>
</div>
Loading

0 comments on commit 205e863

Please sign in to comment.