RGB Widget frontend

This commit is contained in:
Alex Solomaha
2017-02-27 23:22:48 +02:00
parent e56bb465b7
commit f5bb7921bb
14 changed files with 740 additions and 677 deletions

View File

@@ -21,5 +21,6 @@ class MDThemeAsset extends AssetBundle
'yii\web\JqueryAsset',
'yii\bootstrap\BootstrapPluginAsset',
'app\assets\SnackbarjsAsset',
'app\assets\SpectrumAsset',
];
}

View File

@@ -12,6 +12,15 @@ $params = [
'connectionCheckTimeout' => 180,
'connectionCheckMaxIteration' => 2,
],
'items' => [
'rgb' => [
'fade-time' => 10000,
'color-time' => 5000,
'red' => 0,
'green' => 150,
'blue' => 150,
],
],
'telegramBotApiKey' => '',
'telegramBotChatId' => '',
];

24
helpers/RGBHelper.php Normal file
View File

@@ -0,0 +1,24 @@
<?php
namespace app\helpers;
class RGBHelper
{
/**
* @param integer $value
* @return integer
*/
public static function from8to10($value)
{
return $value * 4;
}
/**
* @param integer $value
* @return float|integer
*/
public static function from10to8($value)
{
return floor($value / 4);
}
}

View File

@@ -44,9 +44,19 @@ class Item extends ActiveRecord
const VALUE_ON = 1;
const VALUE_OFF = 0;
/**
* @deprecated
*/
const MODE_RAINBOW = 'rainbow';
/**
* @deprecated
*/
const MODE_BREATH = 'breath';
const RGB_MODE_STATIC = 'static';
const RGB_MODE_WAVE = 'wave';
const RGB_MODE_FADE = 'fade';
/**
* Used for WS handler
* @var mixed
@@ -213,42 +223,28 @@ class Item extends ActiveRecord
/**
* @return array
*/
public static function getModesArray()
public static function getRGBModesArray()
{
return [
self::MODE_RAINBOW,
self::MODE_BREATH,
self::RGB_MODE_STATIC,
self::RGB_MODE_WAVE,
self::RGB_MODE_FADE,
];
}
/**
* Returns normalized default value
* @return mixed
*/
public function getDefaultValue()
public function getDefaultNAValue()
{
if (!is_null($this->default_value)) {
return $this->default_value;
}
switch ($this->type) {
case Item::TYPE_SWITCH:
case Item::TYPE_VARIABLE_BOOLEAN:
case Item::TYPE_VARIABLE_BOOLEAN_DOOR:
return false;
case Item::TYPE_VARIABLE_TEMPERATURE:
case Item::TYPE_VARIABLE_HUMIDITY:
return 0;
case Item::TYPE_RGB:
case self::TYPE_RGB:
return [
0,
0,
0,
'mode' => 'static',
'red' => 0,
'green' => 0,
'blue' => 0,
'fade_time' => Yii::$app->params['items']['rgb']['fade-time'],
];
default:
return 'N/A';
}
return false;
}
}

View File

@@ -189,7 +189,7 @@ class ItemController extends Controller
throw new BadRequestHttpException('This item is not the RGB one');
}
if (!in_array($mode, Item::getModesArray())) {
if (!in_array($mode, Item::getRGBModesArray())) {
throw new InvalidParamException('Unknown mode');
}

View File

@@ -3,6 +3,7 @@
namespace app\servers;
use app\helpers\IPHelper;
use app\helpers\RGBHelper;
use app\models\Board;
use app\models\Event;
use app\models\Setting;
@@ -11,15 +12,18 @@ use app\models\Trigger;
use app\models\History;
use app\models\Item;
use app\models\User;
use MongoDB\Driver\Exception\AuthenticationException;
use Guzzle\Http\QueryString;
use Ratchet\ConnectionInterface;
use Ratchet\MessageComponentInterface;
use Ratchet\WebSocket\Version\RFC6455\Connection;
use React\EventLoop\LoopInterface;
use React\EventLoop\Timer\TimerInterface;
use Yii;
use yii\base\InvalidParamException;
use yii\base\NotSupportedException;
use yii\helpers\ArrayHelper;
use yii\helpers\Json;
use yii\web\BadRequestHttpException;
use yii\web\ForbiddenHttpException;
use yii\web\NotFoundHttpException;
use yii\web\UnauthorizedHttpException;
@@ -40,13 +44,13 @@ class CoreServer implements MessageComponentInterface
* All connected users
* @var ConnectionInterface[]
*/
protected $user_clients;
protected $userConnections;
/**
* All connected boards
* @var ConnectionInterface[]
*/
protected $board_clients;
protected $boardConnections;
/**
* Format like this:
@@ -55,19 +59,19 @@ class CoreServer implements MessageComponentInterface
*
* @var array
*/
protected $item_values;
protected $itemValues;
/**
* `item_id` => TimerInterface
* @var TimerInterface[]
*/
protected $connectionCheckTimers;
protected $pingTimers;
/**
* `item_id` => iteration count
* @var array
*/
protected $connectionCheckIteration;
protected $pingCount;
/**
* @var TimerInterface[]
@@ -85,11 +89,11 @@ class CoreServer implements MessageComponentInterface
{
// Init variables
$this->loop = $loop;
$this->user_clients = [];
$this->board_clients = [];
$this->item_values = [];
$this->connectionCheckTimers = [];
$this->connectionCheckIteration = [];
$this->userConnections = [];
$this->boardConnections = [];
$this->itemValues = [];
$this->pingTimers = [];
$this->pingCount = [];
$this->triggerTimers = [];
// Database driver hack: Prevent MySQL for disconnecting by timeout
@@ -98,168 +102,40 @@ class CoreServer implements MessageComponentInterface
Yii::$app->db->createCommand('SHOW TABLES;')->execute();
});
$this->updateItems();
// Do the startup tasks
// $this->updateItems();
$this->scheduleTriggers();
$this->log('Server started');
}
/**
* @inheritdoc
*/
public function onOpen(ConnectionInterface $conn)
{
$this->log('Managing new connection...');
/** @var Connection $conn */
/** @var QueryString $query */
$query = $conn->WebSocket->request->getQuery();
$type = $query->get('type');
switch ($type) {
case 'user':
$this->log('Connection type is User. Authenticating...');
$userID = $query->get('id');
$userAuthToken = $query->get('auth_token');
if (!$userID or !$userAuthToken) {
$this->log("Invalid credentials: '$userID', '$userAuthToken'");
throw new InvalidParamException("Required fields does not exist");
}
$user = User::findOne([
'id' => $userID,
]);
if (!$user) {
$this->log("User was not found with id '$userID'");
throw new NotFoundHttpException("User was not found with id '$userID'");
}
if ($user->getAuthToken() != $userAuthToken) {
$this->log("User [$user->id] wrong auth token ($userAuthToken and {$user->getAuthToken()})");
throw new AuthenticationException("Wrong credentials");
}
// API request
$api = false;
if ($conn->remoteAddress == '127.0.0.1' and $conn->WebSocket->request->getHeader('Origin') == 'origin') {
$api = true;
}
// Close previous connection if it is not an API connection
if (isset($this->user_clients[$user->id]) and !$api) {
$this->user_clients[$user->id]->close();
}
// Regenerate auth key
$user->reGenerateAuthToken();
// Attach to users
$conn->User = $user;
$conn->api = $api;
$this->user_clients[$user->id] = $conn;
// Prepare Items for User
$items = Item::find()
->active()
->select(['id', 'type', 'room_id', 'board_id', 'default_value'])
->asArray()
->all();
for ($i = 0; $i < count($items); $i++) {
$items[$i]['value'] = $this->getItemSavedValue($items[$i]['id'], $items[$i]['default_value']);
}
$conn->send(Json::encode([
'type' => 'init',
'items' => $items,
]));
$this->logUserConnection($user, true);
return $this->log("Connected user [{$user->id}] {$user->username}");
$this->handleUserConnection($conn, $query);
break;
case 'board':
$this->log('Connection type is Board. Authenticating...');
$boardID = $query->get('id');
$boardSecret = $query->get('secret');
if (!$boardID or !$boardSecret) {
$this->log("Wrong login data: '$boardID' and '$boardSecret'");
throw new UnauthorizedHttpException('Wrong credentials');
}
$board = Board::findOne([
'id' => $boardID,
'type' => Board::TYPE_WEBSOCKET,
'secret' => $boardSecret,
]);
if (!$board) {
$this->log("Board [$boardID] not found!");
throw new NotFoundHttpException("Board with given ID does not exists");
}
if (!$board->remote_connection and !IPHelper::isLocal($conn->remoteAddress)) {
$this->log("Remote connection blocked for board [$boardID]; IP: {$conn->remoteAddress}");
throw new ForbiddenHttpException("Remote connection is not allowed for this Board");
}
// Attach to boards
$conn->Board = $board;
$this->board_clients[$board->id] = $conn;
// Reset previous timer and start a new one
$this->startConnectionCheckTimer($boardID, true);
// Set default values to board's items
foreach ($board->items as $item) {
if ($item->default_value and !is_null($item->default_value)) {
switch ($item->type) {
case Item::TYPE_SWITCH:
$this->sendToBoard($board->id, [
'type' => $item->default_value == 1 ? 'turnON' : 'turnOFF',
'pin' => $item->pin,
]);
break;
case Item::TYPE_RGB:
$rgbData = $this->valueToRgbData($item->default_value);
$red = $rgbData[0];
$green = $rgbData[1];
$blue = $rgbData[2];
$fade = (bool)$rgbData[3];
$this->sendToBoard($board->id, [
'type' => 'rgb',
'red' => $red * 4,
'green' => $green * 4,
'blue' => $blue * 4,
'fade' => $fade,
]);
break;
}
}
}
$this->triggerBoardConnection($board, true);
$this->logBoardConnection($board->id, true);
return $this->log("Connected board [{$board->id}]");
$this->handleBoardConnection($conn, $query);
break;
default:
throw new BadRequestHttpException('Unknown device type');
}
return $this->log('Connection has unknown type. Disconnect');
}
/**
* @inheritdoc
*/
public function onMessage(ConnectionInterface $from, $msg)
{
if (isset($from->User)) {
@@ -275,6 +151,9 @@ class CoreServer implements MessageComponentInterface
return $this->log("Message: '$msg' from unknown client");
}
/**
* @inheritdoc
*/
public function onClose(ConnectionInterface $conn)
{
if (isset($conn->User)) {
@@ -284,12 +163,10 @@ class CoreServer implements MessageComponentInterface
} elseif (isset($conn->Board)) {
$boardId = $conn->Board->id;
$this->log("Disconnecting Board [{$boardId}]...");
// Cancel connection check timer
$this->stopConnectionCheckTimer($boardId);
$this->stopPingTimer($boardId);
unset($this->board_clients[$boardId]);
unset($this->boardConnections[$boardId]);
$this->triggerBoardConnection($conn->Board, false);
@@ -299,6 +176,9 @@ class CoreServer implements MessageComponentInterface
}
}
/**
* @inheritdoc
*/
public function onError(ConnectionInterface $conn, \Exception $e)
{
$this->log("Error: {$e->getMessage()} in file {$e->getFile()} at line {$e->getLine()}");
@@ -307,6 +187,160 @@ class CoreServer implements MessageComponentInterface
$conn->close();
}
/**
* @param Connection $conn
* @param QueryString $query
* @throws UnauthorizedHttpException
*/
protected function handleUserConnection($conn, $query)
{
$userID = $query->get('id');
$userAuthToken = $query->get('auth_token');
$user = User::findOne([
'id' => $userID,
'auth_token' => $userAuthToken,
]);
if (!$user) {
$this->log("Wrong credentials: '$userID', '$userAuthToken''");
throw new UnauthorizedHttpException("Wrong credentials");
}
// Check if it is an API request
$api = $conn->remoteAddress == '127.0.0.1' and $conn->WebSocket->request->getHeader('Origin') == 'origin';
// Close previous connection if it is not an API connection
if (isset($this->userConnections[$user->id]) and !$api) {
$this->userConnections[$user->id]->close();
}
// Regenerate auth token
$user->reGenerateAuthToken();
// Attach to users
$conn->User = $user;
$conn->api = $api;
$this->userConnections[$user->id] = $conn;
// Prepare Items for User
$itemModels = Item::find()
->active()
->all();
$items = [];
foreach ($itemModels as $itemModel) {
$itemData = [];
$itemData['id'] = $itemModel->id;
$itemData['type'] = $itemModel->type;
$itemData['room_id'] = $itemModel->room_id;
$itemData['board_id'] = $itemModel->board_id;
$itemData['pin'] = $itemModel->pin;
$itemData['name'] = $itemModel->name;
$itemData['icon'] = $itemModel->icon;
$itemData['bg'] = $itemModel->bg;
$itemData['class'] = $itemModel->class;
$itemData['sort_order'] = $itemModel->sort_order;
$itemData['value'] = $this->getItemSavedValue($itemModel->id, $itemModel->getDefaultNAValue());
$items[$itemModel->id] = $itemData;
}
$conn->send(Json::encode([
'type' => 'init',
'items' => $items,
]));
$this->logUserConnection($user, true);
$this->log("Connected user [{$user->id}] {$user->username}");
}
/**
* @param ConnectionInterface $conn
* @param QueryString $query
* @throws ForbiddenHttpException
* @throws NotFoundHttpException
* @throws UnauthorizedHttpException
*/
protected function handleBoardConnection(ConnectionInterface $conn, $query)
{
$boardID = $query->get('id');
$boardSecret = $query->get('secret');
if (!$boardID or !$boardSecret) {
$this->log("Wrong board login data: '$boardID' and '$boardSecret'");
throw new UnauthorizedHttpException('Wrong credentials');
}
$board = Board::findOne([
'id' => $boardID,
'type' => Board::TYPE_WEBSOCKET,
'secret' => $boardSecret,
]);
if (!$board) {
$this->log("Board [$boardID] not found!");
throw new NotFoundHttpException("Board with given ID does not exists");
}
if (!$board->remote_connection and !IPHelper::isLocal($conn->remoteAddress)) {
$this->log("Remote connection blocked for board [$boardID]; IP: {$conn->remoteAddress}");
throw new ForbiddenHttpException("Remote connection is not allowed for this Board");
}
// Attach to boards
$conn->Board = $board;
$this->boardConnections[$board->id] = $conn;
// Reset previous timer and start a new one
$this->startPingTimer($boardID, true);
// Set default values to board's items
// foreach ($board->items as $item) {
// if ($item->default_value and !is_null($item->default_value)) {
// switch ($item->type) {
// case Item::TYPE_SWITCH:
// $this->sendToBoard($board->id, [
// 'type' => $item->default_value == 1 ? 'turnON' : 'turnOFF',
// 'pin' => $item->pin,
// ]);
//
// break;
// case Item::TYPE_RGB:
// $rgbData = $this->valueToRgbData($item->default_value);
//
// $red = $rgbData[0];
// $green = $rgbData[1];
// $blue = $rgbData[2];
// $fade = (bool)$rgbData[3];
//
// $this->sendToBoard($board->id, [
// 'type' => 'rgb',
// 'red' => $red * 4,
// 'green' => $green * 4,
// 'blue' => $blue * 4,
// 'fade' => $fade,
// ]);
//
// break;
// }
// }
// }
$this->triggerBoardConnection($board, true);
$this->logBoardConnection($board->id, true);
$this->log("Connected board [{$board->id}]");
}
/**
* @param $from
* @param $msg
@@ -328,8 +362,6 @@ class CoreServer implements MessageComponentInterface
return $this->handleTurnOff($from, $user, $data);
case 'rgb':
return $this->handleRgb($from, $user, $data);
case 'rgbMode':
return $this->handleRgbMode($from, $user, $data);
case 'schedule-triggers':
return $this->scheduleTriggers();
case 'update-items':
@@ -345,6 +377,7 @@ class CoreServer implements MessageComponentInterface
* @param $from
* @param $msg
* @return bool
* @throws NotFoundHttpException
*/
public function handleBoardMessage($from, $msg)
{
@@ -352,8 +385,8 @@ class CoreServer implements MessageComponentInterface
$board = $from->Board;
$data = Json::decode($msg);
// Board responds: restart connection check timer
$this->startConnectionCheckTimer($board->id);
// Restart ping timer
$this->startPingTimer($board->id);
switch ($data['type']) {
case 'value':
@@ -385,42 +418,67 @@ class CoreServer implements MessageComponentInterface
$this->logItemValue($item, $value);
break;
case 'rgbMode':
/**
* Message structure:
* {"start":true,"type":"rgbMode","mode":"rainbow","pin":1}
*/
$value = $data['mode'];
$pin = (integer)$data['pin'];
$start = (bool)$data['start'];
case 'rgb':
$itemId = (integer)$data['item_id'];
$mode = $data['mode'];
$fadeTime = (int)$data['fade_time'];
$item = Item::findOne([
'id' => $itemId,
'board_id' => $board->id,
'pin' => $pin,
]);
if (!$item) {
return $this->log('Trying to use unknown item');
$this->log("Board [{$board->id}] tried to use unknown item");
throw new NotFoundHttpException('Item does not exist');
}
if ($start) {
$value = $this->saveItemValue($item->id, $value, $item->type, false);
} else {
$value = $this->saveItemValue($item->id, $item->getDefaultValue(), $item->type);
if (!in_array($mode, Item::getRGBModesArray())) {
$this->log("Board [{$board->id}] tried to use unknown RGB mode");
throw new InvalidParamException('Unknown RGB mode');
}
// TODO: trigger
if ($fadeTime < 0) {
$fadeTime = 0;
}
$this->sendUsers([
'type' => 'value',
$commonParameters = [
'type' => 'rgb',
'item_id' => $item->id,
'item_type' => $item->type,
'value' => $value,
'start' => $start,
]);
'mode' => $mode,
'fade_time' => $fadeTime,
];
// Save to history
$this->logItemValue($item, $start ? 'start:' : '' . $value);
$modeParameters = [];
if ($mode === Item::RGB_MODE_STATIC or $mode === Item::RGB_MODE_FADE) {
// Fill saved values if not provided
$red = isset($data['red']) ? $data['red'] : Yii::$app->params['items']['rgb']['red'];
$green = isset($data['green']) ? $data['green'] : Yii::$app->params['items']['rgb']['green'];
$blue = isset($data['blue']) ? $data['blue'] : Yii::$app->params['items']['rgb']['blue'];
// Convert color from 1023 to 255
$red = RGBHelper::from10to8($red);
$green = RGBHelper::from10to8($green);
$blue = RGBHelper::from10to8($blue);
$modeParameters['red'] = $red;
$modeParameters['green'] = $green;
$modeParameters['blue'] = $blue;
}
if ($mode === Item::RGB_MODE_WAVE or $mode === Item::RGB_MODE_FADE) {
$colorTime = isset($data['color_time']) ? $data['color_time'] : Yii::$app->params['items']['rgb']['color-time'];
$modeParameters['color_time'] = $colorTime;
}
$parameters = ArrayHelper::merge($commonParameters, $modeParameters);
$this->sendUsers($parameters);
$this->logItemValue($item, serialize($parameters));
$this->saveItemValue($item->id, $parameters, $item->type);
break;
case 'values':
@@ -600,108 +658,66 @@ class CoreServer implements MessageComponentInterface
]);
}
$red = $data['red'];
$green = $data['green'];
$blue = $data['blue'];
if ($red > 255) {
$red = 255;
}
if ($green > 255) {
$green = 255;
}
if ($blue > 255) {
$blue = 255;
}
$board = $item->board;
switch ($board->type) {
case Board::TYPE_AREST:
throw new NotSupportedException();
case Board::TYPE_WEBSOCKET:
if (!$this->isBoardConnected($board->id)) {
return $from->send(Json::encode([
'type' => 'error',
'message' => 'Устройство не подключено',
]));
}
$fade = isset($data['fade']) ? (bool)$data['fade'] : false;
$this->sendToBoard($board->id, [
'type' => 'rgb',
'red' => $red * 4,
'green' => $green * 4,
'blue' => $blue * 4,
'fade' => $fade,
]);
break;
}
// $rgbArray = [
// $red,
// $green,
// $blue
// ];
// $this->item_values[$item->id]['value'] = $rgbArray;
$history = new History();
$history->type = History::TYPE_USER_ACTION;
$history->user_id = $user->id;
$history->item_id = $item->id;
$history->commited_at = time();
$history->value = $red . ',' . $green . ',' . $blue;
if (!$history->save()) {
$this->log("Cannot log: ");
var_dump($history->errors);
}
return true;
}
/**
* @param ConnectionInterface $from
* @param User $user
* @param array $data
* @return bool|mixed
* @throws NotSupportedException
*/
protected function handleRgbMode($from, $user, $data)
{
$item_id = (int)$data['item_id'];
$item = Item::findOne($item_id);
if (!$item) {
return $from->send(Json::encode([
'type' => 'error',
'message' => 'Такое устройство не существует',
]));
}
if ($item->type !== Item::TYPE_RGB) {
return $from->send(Json::encode([
'type' => 'error',
'message' => 'Данный тип устройства не является RGB',
]));
}
$mode = $data['mode'];
$start = (bool)$data['start'];
$fadeTime = isset($data['fade_time']) ? $data['fade_time'] : Yii::$app->params['items']['rgb']['fade-time'];
if (!in_array($mode, Item::getModesArray())) {
return $from->send(Json::encode([
'type' => 'error',
'message' => 'Неизвестный режим',
]));
if (!in_array($mode, Item::getRGBModesArray())) {
throw new InvalidParamException('Unknown RGB mode');
}
if ($fadeTime < 0) {
$fadeTime = 0;
}
$commonParameters = [
'type' => 'rgb',
'item_id' => $item->id,
'mode' => $mode,
'fade_time' => $fadeTime,
];
$modeParameters = [];
if ($mode === Item::RGB_MODE_STATIC or $mode === Item::RGB_MODE_FADE) {
// Fill saved values if not provided
$red = isset($data['red']) ? $data['red'] : Yii::$app->params['items']['rgb']['red'];
$green = isset($data['green']) ? $data['green'] : Yii::$app->params['items']['rgb']['green'];
$blue = isset($data['blue']) ? $data['blue'] : Yii::$app->params['items']['rgb']['blue'];
// Convert color from 255 to 1023
if ($red > 255) {
$red = 255;
}
if ($green > 255) {
$green = 255;
}
if ($blue > 255) {
$blue = 255;
}
$red = RGBHelper::from8to10($red);
$green = RGBHelper::from8to10($green);
$blue = RGBHelper::from8to10($blue);
$modeParameters['red'] = $red;
$modeParameters['green'] = $green;
$modeParameters['blue'] = $blue;
}
if ($mode === Item::RGB_MODE_WAVE or $mode === Item::RGB_MODE_FADE) {
$colorTime = isset($data['color_time']) ? $data['color_time'] : Yii::$app->params['items']['rgb']['color-time'];
if ($colorTime < 0) {
$colorTime = 0;
}
$modeParameters['color_time'] = $colorTime;
}
$parameters = ArrayHelper::merge($commonParameters, $modeParameters);
$board = $item->board;
switch ($board->type) {
@@ -716,11 +732,7 @@ class CoreServer implements MessageComponentInterface
]));
}
$this->sendToBoard($board->id, [
'type' => 'rgbMode',
'mode' => $mode,
'action' => $start ? 'start' : 'stop',
]);
$this->sendToBoard($board->id, $parameters);
break;
}
@@ -730,10 +742,10 @@ class CoreServer implements MessageComponentInterface
$history->user_id = $user->id;
$history->item_id = $item->id;
$history->commited_at = time();
$history->value = $mode . ', ' . $start ? 'start' : 'stop';
$history->value = serialize($parameters);
if (!$history->save()) {
$this->log("Cannot log: ");
$this->log("Cannot log:");
var_dump($history->errors);
}
@@ -792,7 +804,7 @@ class CoreServer implements MessageComponentInterface
{
$msg = Json::encode($data);
foreach ($this->user_clients as $client) {
foreach ($this->userConnections as $client) {
$client->send($msg);
}
}
@@ -802,24 +814,21 @@ class CoreServer implements MessageComponentInterface
*
* @param integer $board_id
* @param array $data
* @return bool|ConnectionInterface
*/
protected function sendToBoard($board_id, $data)
{
if (isset($this->board_clients[$board_id])) {
if (isset($this->boardConnections[$board_id])) {
/** @var ConnectionInterface $client */
$client = $this->board_clients[$board_id];
$client = $this->boardConnections[$board_id];
$msg = Json::encode($data);
$this->log("Sending to board [$board_id]: $msg");
return $client->send($msg);
$client->send($msg);
} else {
$this->log("Cannot send to board [$board_id]: not connected");
}
$this->log("Cannot send to board [$board_id]: not connected");
return false;
}
/**
@@ -852,7 +861,7 @@ class CoreServer implements MessageComponentInterface
*/
protected function isBoardConnected($boardID)
{
return isset($this->board_clients[$boardID]);
return isset($this->boardConnections[$boardID]);
}
/**
@@ -973,7 +982,7 @@ class CoreServer implements MessageComponentInterface
$this->log("Connection check for Board [$boardID]...");
// Check if it is already disconnected
if (!isset($this->board_clients[$boardID])) {
if (!isset($this->boardConnections[$boardID])) {
$this->logBoardConnection($boardID, false);
$this->log("Board [$boardID] has already been disconnected");
@@ -981,18 +990,18 @@ class CoreServer implements MessageComponentInterface
return;
}
if (isset($this->connectionCheckIteration[$boardID])) {
if ($this->connectionCheckIteration[$boardID] >= Yii::$app->params['server']['connectionCheckMaxIteration']) {
if (isset($this->pingCount[$boardID])) {
if ($this->pingCount[$boardID] >= Yii::$app->params['server']['connectionCheckMaxIteration']) {
$this->log("Maximum ignored ping commands reached. Disconnecting...");
$this->board_clients[$boardID]->close();
$this->boardConnections[$boardID]->close();
return;
}
$this->connectionCheckIteration[$boardID]++;
$this->pingCount[$boardID]++;
} else {
$this->connectionCheckIteration[$boardID] = 1;
$this->pingCount[$boardID] = 1;
}
$this->sendToBoard($boardID, [
@@ -1356,30 +1365,35 @@ class CoreServer implements MessageComponentInterface
}
/**
* Checks if item value is stored and if so returns it.
* If value is missing - returns default from parameter
*
* @param int $item_id
* @param mixed $defaultValue
* @return mixed
*/
protected function getItemSavedValue($item_id, $defaultValue = false)
protected function getItemSavedValue($item_id, $defaultValue = null)
{
if ($this->hasItemSavedValue($item_id)) {
return $this->item_values[$item_id];
return $this->itemValues[$item_id];
}
return $defaultValue;
}
/**
* Checks if item value is stored.
*
* @param int $item_id
* @return bool
*/
protected function hasItemSavedValue($item_id)
{
return isset($this->item_values[$item_id]);
return isset($this->itemValues[$item_id]) and $this->itemValues[$item_id] !== null;
}
/**
* Saves to value array and returns it. Normalization is on by default
* Saves to value array and returns it. Normalization is enabled by default
*
* @param int $item_id
* @param mixed $value
@@ -1393,7 +1407,7 @@ class CoreServer implements MessageComponentInterface
$value = $this->normalizeItemValue($value, $item_type);
}
$this->item_values[$item_id] = $value;
$this->itemValues[$item_id] = $value;
return $value;
}
@@ -1414,9 +1428,6 @@ class CoreServer implements MessageComponentInterface
case Item::TYPE_VARIABLE_TEMPERATURE:
case Item::TYPE_VARIABLE_HUMIDITY:
return (int)$value;
case Item::TYPE_RGB:
return $this->valueToRgb($value);
}
return $value;
@@ -1430,14 +1441,14 @@ class CoreServer implements MessageComponentInterface
* @param int $boardID
* @param bool $stopPrevious
*/
protected function startConnectionCheckTimer($boardID, $stopPrevious = true)
protected function startPingTimer($boardID, $stopPrevious = true)
{
if ($stopPrevious) {
$this->stopConnectionCheckTimer($boardID);
$this->stopPingTimer($boardID);
}
// Start connection checks
$this->connectionCheckTimers[$boardID] = $this->loop->addPeriodicTimer(
$this->pingTimers[$boardID] = $this->loop->addPeriodicTimer(
Yii::$app->params['server']['connectionCheckTimeout'],
function () use ($boardID) {
$this->doConnectionCheckTimer($boardID);
@@ -1451,18 +1462,18 @@ class CoreServer implements MessageComponentInterface
* @param int $boardID
* @param bool $resetCount
*/
protected function stopConnectionCheckTimer($boardID, $resetCount = true)
protected function stopPingTimer($boardID, $resetCount = true)
{
if (isset($this->connectionCheckTimers[$boardID])) {
if ($this->connectionCheckTimers[$boardID] instanceof TimerInterface) {
$this->connectionCheckTimers[$boardID]->cancel();
if (isset($this->pingTimers[$boardID])) {
if ($this->pingTimers[$boardID] instanceof TimerInterface) {
$this->pingTimers[$boardID]->cancel();
}
unset($this->connectionCheckTimers[$boardID]);
unset($this->pingTimers[$boardID]);
}
if ($resetCount and isset($this->connectionCheckIteration[$boardID])) {
unset($this->connectionCheckIteration[$boardID]);
if ($resetCount and isset($this->pingCount[$boardID])) {
unset($this->pingCount[$boardID]);
}
}
}

View File

@@ -7,7 +7,7 @@ use rmrevin\yii\fontawesome\FA;
?>
<div class="col-lg-2 col-md-4 col-sm-6">
<div class="col-lg-3 col-md-4 col-sm-6">
<div class="panel-item panel-item-rgb withripple <?= $item->class ?>" data-item-id="<?= $item->id ?>">
<div class="panel-item-rgb-icon">
<?= FA::i($item->icon) ?>

View File

@@ -14,6 +14,6 @@ use rmrevin\yii\fontawesome\FA;
<div class="item-variable-icon">
<?= FA::i($item->icon) ?>
</div>
<div class="item-variable-value item-value"><?= $item->getDefaultValue() ?></div>
<div class="item-variable-value item-value"><?= $item->getDefaultNAValue() ?></div>
</div>
</div>

View File

@@ -72,27 +72,89 @@ $this->title = 'Панель Управления';
</main>
<script id="rgb-item-widget-popover-content" type="text/x-handlebars-template">
<ul class="nav nav-tabs nav-justified">
<li class="active">
<a href="#rgb-color" data-toggle="tab" aria-expanded="true">
Цвет
</a>
</li>
<li>
<a href="#rgb-mode" data-toggle="tab" aria-expanded="false">
Режим
</a>
</li>
</ul>
<div class="tab-content">
<div class="tab-pane fade in active" id="rgb-color">
<input type="text"
id="colorpicker-{{item_id}}"
class="rgb-colorpicker"
data-item-id="{{item_id}}">
</div>
<div class="tab-pane fade" id="rgb-mode">
<p>Выберите режим</p>
<div class="rgb-widget-popover-content" data-item-id="{{item_id}}">
<ul class="nav nav-tabs nav-justified">
<li class="active">
<a href="#rgb-widget-static" class="rgb-widget-mode rgb-widget-mode-static" data-toggle="tab"
aria-expanded="true">
Static
</a>
</li>
<li>
<a href="#rgb-widget-wave" class="rgb-widget-mode rgb-widget-mode-wave" data-toggle="tab"
aria-expanded="false">
Wave
</a>
</li>
<li>
<a href="#rgb-widget-fade" class="rgb-widget-mode rgb-widget-mode-fade" data-toggle="tab"
aria-expanded="false">
Fade
</a>
</li>
</ul>
<div class="tab-content">
<div class="tab-pane fade in active" id="rgb-widget-static">
<form class="rgb-widget-form rgb-widget-form-static">
<div class="row">
<div class="col-sm-6">
<label for="colorpicker-{{item_id}}" class="hidden">Цвет</label>
<input type="text"
class="rgb-widget-colorpicker rgb-widget-colorpicker-static"
data-item-id="{{item_id}}"
id="colorpicker-{{item_id}}">
</div>
<div class="col-sm-6">
<div class="form-group">
<label for="rgb-widget-static-fade-time">Время перехода (мс)</label>
<input type="number" min="0" step="500" class="form-control" id="rgb-widget-static-fade-time"
value="<?= Yii::$app->params['items']['rgb']['fade-time'] ?>">
</div>
</div>
</div>
</form>
</div>
<div class="tab-pane fade" id="rgb-widget-wave">
<form class="rgb-widget-form rgb-widget-form-wave">
<div class="form-group">
<label for="rgb-widget-wave-fade-time">Время перехода (мс)</label>
<input type="number" min="0" step="500" class="form-control" id="rgb-widget-wave-fade-time"
value="<?= Yii::$app->params['items']['rgb']['fade-time'] ?>">
</div>
<div class="form-group">
<label for="rgb-widget-wave-color-time">Время цвета (мс)</label>
<input type="number" min="0" step="500" class="form-control" id="rgb-widget-wave-color-time"
value="<?= Yii::$app->params['items']['rgb']['color-time'] ?>">
</div>
<button class="btn btn-primary btn-save-times" data-mode="wave">Применить</button>
</form>
</div>
<div class="tab-pane fade" id="rgb-widget-fade">
<form class="rgb-widget-form rgb-widget-form-fade">
<div class="row">
<div class="col-sm-6">
<label for="fade-colorpicker-{{item_id}}" class="hidden">Цвет</label>
<input type="text"
class="rgb-widget-colorpicker rgb-widget-colorpicker-fade"
data-item-id="{{item_id}}"
id="fade-colorpicker-{{item_id}}">
</div>
<div class="col-sm-6">
<div class="form-group">
<label for="rgb-widget-fade-fade-time">Время перехода (мс)</label>
<input type="number" min="0" step="500" class="form-control" id="rgb-widget-fade-fade-time"
value="<?= Yii::$app->params['items']['rgb']['fade-time'] ?>">
</div>
<div class="form-group">
<label for="rgb-widget-fade-color-time">Время цвета (мс)</label>
<input type="number" min="0" step="500" class="form-control" id="rgb-widget-fade-color-time"
value="<?= Yii::$app->params['items']['rgb']['color-time'] ?>">
</div>
<button class="btn btn-primary btn-save-times" data-mode="fade">Применить</button>
</div>
</div>
</form>
</div>
</div>
</div>
</script>

View File

@@ -4,6 +4,3 @@
$this->title = 'Главная';
?>
<div class="site-index">
<h1 class="page-header"><?= $this->title ?></h1>
</div>

View File

@@ -183,7 +183,7 @@ body {
display: block;
position: relative;
height: 1px;
width:100%;
width: 100%;
background: #efefef;
margin: 10px 0;
}
@@ -290,7 +290,7 @@ body {
}
.panel-item.panel-item-rgb {
background: #909090;
background: #009688;
border-radius: 30px;
color: #fff;
margin: 10px 0;
@@ -300,15 +300,12 @@ body {
height: 50px;
width: 100%;
box-shadow: 0 1px 1.5px 0 rgba(0, 0, 0, .12), 0 1px 1px 0 rgba(0, 0, 0, .24);
transition: 0.5s;
transition: 1s;
user-select: none;
}
.popover {
position: fixed;
z-index: 1000;
width: 300px;
/*height: 200px;*/
box-shadow: 0px 5px 5px -3px rgba(0, 0, 0, 0.2), 0px 8px 10px 1px rgba(0, 0, 0, 0.14), 0px 3px 14px 2px rgba(0, 0, 0, 0.12);
max-width: none;
border-radius: 0;
@@ -323,6 +320,35 @@ body {
padding: 0;
}
.rgb-widget-form {
padding: 10px;
}
.sp-container {
display: block;
border-radius: 0;
background-color: transparent;
border: none;
padding: 0;
}
.sp-picker-container {
width: 100%;
border: none;
}
.sp-picker-container, .sp-palette-container {
float: none;
padding: 2px 0 0 0;
margin: 0;
}
@media (min-width: 767px) {
.popover {
width: 500px;
}
}
/* Snackbar fixes */
#snackbar-container {
position: fixed;
@@ -442,7 +468,7 @@ body.login-body {
color: #666;
}
.material-table thead a:hover,.material-table thead a:active {
.material-table thead a:hover, .material-table thead a:active {
color: #373737;
text-decoration: none;
}

View File

@@ -1,5 +1,3 @@
/* Content loader */
#loader {
position: relative;

View File

@@ -5966,28 +5966,6 @@ button.close {
-webkit-box-shadow: 2px 2px 4px rgba(0, 0, 0, 0.4);
box-shadow: 2px 2px 4px rgba(0, 0, 0, 0.4);
}
.btn-default:after {
content: "";
display: block;
position: absolute;
width: 100%;
height: 100%;
top: 0;
left: 0;
background-image: -webkit-radial-gradient(circle, #444444 10%, transparent 10.01%);
background-image: radial-gradient(circle, #444444 10%, transparent 10.01%);
background-repeat: no-repeat;
background-size: 1000% 1000%;
background-position: 50%;
opacity: 0;
pointer-events: none;
transition: background .5s, opacity 1s;
}
.btn-default:active:after {
background-size: 0% 0%;
opacity: .2;
transition: 0s;
}
.btn-primary {
position: relative;
}
@@ -6002,28 +5980,6 @@ button.close {
-webkit-box-shadow: 2px 2px 4px rgba(0, 0, 0, 0.4);
box-shadow: 2px 2px 4px rgba(0, 0, 0, 0.4);
}
.btn-primary:after {
content: "";
display: block;
position: absolute;
width: 100%;
height: 100%;
top: 0;
left: 0;
background-image: -webkit-radial-gradient(circle, #ffffff 10%, transparent 10.01%);
background-image: radial-gradient(circle, #ffffff 10%, transparent 10.01%);
background-repeat: no-repeat;
background-size: 1000% 1000%;
background-position: 50%;
opacity: 0;
pointer-events: none;
transition: background .5s, opacity 1s;
}
.btn-primary:active:after {
background-size: 0% 0%;
opacity: .2;
transition: 0s;
}
.btn-success {
position: relative;
}
@@ -6038,28 +5994,6 @@ button.close {
-webkit-box-shadow: 2px 2px 4px rgba(0, 0, 0, 0.4);
box-shadow: 2px 2px 4px rgba(0, 0, 0, 0.4);
}
.btn-success:after {
content: "";
display: block;
position: absolute;
width: 100%;
height: 100%;
top: 0;
left: 0;
background-image: -webkit-radial-gradient(circle, #ffffff 10%, transparent 10.01%);
background-image: radial-gradient(circle, #ffffff 10%, transparent 10.01%);
background-repeat: no-repeat;
background-size: 1000% 1000%;
background-position: 50%;
opacity: 0;
pointer-events: none;
transition: background .5s, opacity 1s;
}
.btn-success:active:after {
background-size: 0% 0%;
opacity: .2;
transition: 0s;
}
.btn-info {
position: relative;
}
@@ -6074,28 +6008,6 @@ button.close {
-webkit-box-shadow: 2px 2px 4px rgba(0, 0, 0, 0.4);
box-shadow: 2px 2px 4px rgba(0, 0, 0, 0.4);
}
.btn-info:after {
content: "";
display: block;
position: absolute;
width: 100%;
height: 100%;
top: 0;
left: 0;
background-image: -webkit-radial-gradient(circle, #ffffff 10%, transparent 10.01%);
background-image: radial-gradient(circle, #ffffff 10%, transparent 10.01%);
background-repeat: no-repeat;
background-size: 1000% 1000%;
background-position: 50%;
opacity: 0;
pointer-events: none;
transition: background .5s, opacity 1s;
}
.btn-info:active:after {
background-size: 0% 0%;
opacity: .2;
transition: 0s;
}
.btn-warning {
position: relative;
}
@@ -6110,28 +6022,6 @@ button.close {
-webkit-box-shadow: 2px 2px 4px rgba(0, 0, 0, 0.4);
box-shadow: 2px 2px 4px rgba(0, 0, 0, 0.4);
}
.btn-warning:after {
content: "";
display: block;
position: absolute;
width: 100%;
height: 100%;
top: 0;
left: 0;
background-image: -webkit-radial-gradient(circle, #ffffff 10%, transparent 10.01%);
background-image: radial-gradient(circle, #ffffff 10%, transparent 10.01%);
background-repeat: no-repeat;
background-size: 1000% 1000%;
background-position: 50%;
opacity: 0;
pointer-events: none;
transition: background .5s, opacity 1s;
}
.btn-warning:active:after {
background-size: 0% 0%;
opacity: .2;
transition: 0s;
}
.btn-danger {
position: relative;
}
@@ -6146,28 +6036,6 @@ button.close {
-webkit-box-shadow: 2px 2px 4px rgba(0, 0, 0, 0.4);
box-shadow: 2px 2px 4px rgba(0, 0, 0, 0.4);
}
.btn-danger:after {
content: "";
display: block;
position: absolute;
width: 100%;
height: 100%;
top: 0;
left: 0;
background-image: -webkit-radial-gradient(circle, #ffffff 10%, transparent 10.01%);
background-image: radial-gradient(circle, #ffffff 10%, transparent 10.01%);
background-repeat: no-repeat;
background-size: 1000% 1000%;
background-position: 50%;
opacity: 0;
pointer-events: none;
transition: background .5s, opacity 1s;
}
.btn-danger:active:after {
background-size: 0% 0%;
opacity: .2;
transition: 0s;
}
.btn-link {
position: relative;
}

View File

@@ -41,7 +41,15 @@ function onMessage(e) {
switch (data.type) {
case 'init':
$.each(data.items, function (key, value) {
updateItemValue(value.id, value.type, value.value)
if (value.type == 30) {
updateRGB(value.id, value.value);
} else {
if (value.value != 'N/A') {
saveItemValue(id, value.value);
}
updateItemValue(value.id, value.type, value.value);
}
});
afterConnected();
@@ -68,13 +76,15 @@ function afterConnected() {
});
}
function send(msg) {
if (typeof msg != "string") {
msg = JSON.stringify(msg);
function send(data) {
console.log(data);
if (typeof data != "string") {
data = JSON.stringify(data);
}
if (WS && WS.readyState == 1) {
WS.send(msg);
WS.send(data);
}
}
@@ -82,6 +92,20 @@ function updateValue(data) {
updateItemValue(data.item_id, data.item_type, data.value);
}
function updateRGB(itemId, data) {
itemValues[itemId] = data;
if (data.mode == 'static' || data.mode == 'fade') {
$('.panel-item-rgb[data-item-id="' + itemId + '"]').attr('style', 'background: rgb(' + data.red + ',' + data.green + ',' + data.blue + ')');
}
$('#rgb-widget-wave-fade-time').val(data.fade_time);
if (data.mode == 'fade' || data.mode == 'wave') {
$('#rgb-widget-wave-color-time').val(data.color_time);
}
}
function itemSwitchOn(itemId) {
var $item = $('.panel-item-switch[data-item-id="' + itemId + '"]');
$item.removeClass('off');
@@ -96,12 +120,22 @@ function itemSetValue(itemId, value) {
$('.panel-item-variable[data-item-id="' + itemId + '"] > .item-variable-value').html(value);
}
function saveItemValue(itemId, value) {
itemValues[itemId] = value;
}
function getSavedItemValue(itemId) {
return itemValues[itemId];
}
function updateItemValue(id, type, value) {
type = parseInt(type);
switch (type) {
case 10: // Switch
if (Boolean(value)) {
value = Boolean(value);
if (value) {
itemSwitchOn(id);
} else {
itemSwitchOff(id);
@@ -118,189 +152,226 @@ function updateItemValue(id, type, value) {
itemSetValue(id, value + '%');
break;
case 25: // Variable boolean
value = Boolean(value) ? 'да' : 'нет';
if (Boolean(value)) {
if (value != 'N/A') {
value = 'да';
}
} else {
value = 'нет';
}
itemSetValue(id, value);
break;
case 26: // Variable boolean door
if (value) {
value = 'открыто';
if (value != 'N/A') {
value = 'открыто';
}
} else {
value = 'закрыто';
}
itemSetValue(id, value);
break;
case 30: // RGB
itemValues[id] = value;
// TODO
// if (typeof value === 'string') {
// $('.item-rgb[data-item-id="' + id + '"]')
// .find('.rgb-mode[data-mode="' + value + '"]')
// .addClass('active');
// } else {
// var $colorPicker = $('#colorpicker-' + id);
//
// $colorPicker.spectrum('set', 'rgb(' + value[0] + ', ' + value[1] + ', ' + value[2] + ')');
//
// $('.item-rgb .rgb-mode').removeClass('active');
// }
break;
}
}
$(document).ready(function () {
// Event listeners
$('.panel-item-switch').click(function (e) {
e.preventDefault();
var item_id = $(this).data('item-id');
var action = $(this).hasClass('off') ? 'turnON' : 'turnOFF';
var $this = $(this);
var item_id = $this.data('item-id');
send({
"type": action,
"type": $this.hasClass('off') ? 'turnON' : 'turnOFF',
"item_id": item_id
});
return false;
});
$('.panel-item-variable').tooltip();
$('.panel-item-rgb').popover({
html: true,
placement: 'bottom',
trigger: 'click',
content: function () {
var source = $("#rgb-item-widget-popover-content").html();
var template = Handlebars.compile(source);
// RGB Widget
$('.panel-item-rgb')
.popover({
html: true,
placement: 'bottom',
trigger: 'click',
container: 'body',
content: function () {
var source = $("#rgb-item-widget-popover-content").html();
var template = Handlebars.compile(source);
return template({
item_id: $(this).data('item-id')
return template({
item_id: $(this).data('item-id')
});
}
})
// On popover init
.on('inserted.bs.popover', function () {
var item_id = $(this).data('item-id');
// Init colorpicker
var $colorPicker = $('body').find('.rgb-widget-colorpicker[data-item-id="' + item_id + '"]').spectrum({
flat: true,
showInput: false,
showButtons: false,
preferredFormat: 'rgb'
});
}
}).on('inserted.bs.popover', function (e) {
var $colorPicker = $(this).parent().find('input').spectrum({
flat: true,
showInput: true,
showButtons: false,
preferredFormat: 'rgb',
dragstop: function (color) {
console.log($(this));
var item_id = $(this).data('item-id');
var savedItemValue = getSavedItemValue(item_id);
// Set colorpicker value
if (savedItemValue != null) {
$colorPicker.spectrum('set', 'rgb(' + savedItemValue['red'] + ',' + savedItemValue['green'] + ',' + savedItemValue['blue'] + ')');
}
// Set mode and variables
if (savedItemValue.mode == 'static') {
$('.rgb-widget-mode-static').tab('show');
} else if (savedItemValue.mode == 'wave') {
$('#rgb-widget-wave-color-time').val(savedItemValue.color_time);
$('#rgb-widget-fade-color-time').val(savedItemValue.color_time);
$('.rgb-widget-mode-wave').tab('show');
} else if (savedItemValue.mode == 'fade') {
$('#rgb-widget-wave-color-time').val(savedItemValue.color_time);
$('#rgb-widget-fade-color-time').val(savedItemValue.color_time);
$('.rgb-widget-mode-fade').tab('show');
}
$('#rgb-widget-static-fade-time').val(savedItemValue.fade_time);
$('#rgb-widget-wave-fade-time').val(savedItemValue.fade_time);
$('#rgb-widget-fade-fade-time').val(savedItemValue.fade_time);
// On color select
$colorPicker.on("dragstop.spectrum", function (e, color) {
var red = Math.round(color._r);
var green = Math.round(color._g);
var blue = Math.round(color._b);
//
// var fade = ($('.fade-checkbox[data-item-id="' + item_id + '"]:checked').length > 0);
//
// send({
// 'type': 'rgb',
// 'item_id': item_id,
// 'fade': fade,
// 'red': red,
// 'green': green,
// 'blue': blue
// });
$('.panel-item-rgb[data-item-id="' + item_id + '"]').attr('style', 'background: rgb(' + red + ',' + green + ',' + blue + ')');
}
var $this = $(this);
var itemId = $this.data('item-id');
$('.panel-item-rgb[data-item-id="' + itemId + '"]')
.attr('style', 'background-color: rgb(' + red + ',' + green + ',' + blue + ')');
var modeId = $this.parents('.tab-pane').attr('id');
if (modeId == 'rgb-widget-static') {
send({
"type": "rgb",
"item_id": item_id,
"fade_time": parseInt($('#rgb-widget-static-fade-time').val()),
"mode": "static",
"red": red,
"green": green,
"blue": blue
});
} else if (modeId == 'rgb-widget-fade') {
send({
"type": "rgb",
"item_id": item_id,
"fade_time": parseInt($('#rgb-widget-fade-fade-time').val()),
"color_time": parseInt($('#rgb-widget-fade-color-time').val()),
"mode": "fade",
"red": red,
"green": green,
"blue": blue
});
}
});
$('.btn-save-times').click(function (e) {
e.preventDefault();
var mode = $(this).data('mode');
if (mode == 'wave') {
send({
"type": "rgb",
"item_id": item_id,
"mode": "wave",
"fade_time": parseInt($('#rgb-widget-wave-fade-time').val()),
"color_time": parseInt($('#rgb-widget-wave-color-time').val())
});
} else if (mode == 'fade') {
var color = $('.rgb-widget-colorpicker-fade').spectrum('get');
var red = Math.round(color._r);
var green = Math.round(color._g);
var blue = Math.round(color._b);
if (savedItemValue != null) {
send({
"type": "rgb",
"item_id": item_id,
"mode": "fade",
"fade_time": parseInt($('#rgb-widget-fade-fade-time').val()),
"color_time": parseInt($('#rgb-widget-fade-color-time').val()),
"red": red,
"green": green,
"blue": blue
});
} else {
send({
"type": "rgb",
"item_id": item_id,
"mode": "fade",
"fade_time": parseInt($('#rgb-widget-fade-fade-time').val()),
"color_time": parseInt($('#rgb-widget-fade-color-time').val())
});
}
}
});
$('.rgb-widget-popover-content[data-item-id="' + item_id + '"]').on('click', '.rgb-widget-mode', function (e) {
var $this = $(this);
if ($this.attr('aria-expanded') == 'true') {
return;
}
if ($this.hasClass('rgb-widget-mode-static')) {
var color = $('.rgb-widget-colorpicker-static').spectrum('get');
var red = Math.round(color._r);
var green = Math.round(color._g);
var blue = Math.round(color._b);
send({
"type": "rgb",
"item_id": item_id,
"fade_time": parseInt($('#rgb-widget-static-fade-time').val()),
"mode": "static",
"red": red,
"green": green,
"blue": blue
});
} else if ($this.hasClass('rgb-widget-mode-wave')) {
send({
"type": "rgb",
"item_id": item_id,
"mode": "wave",
"fade_time": parseInt($('#rgb-widget-wave-fade-time').val()),
"color_time": parseInt($('#rgb-widget-wave-color-time').val())
});
} else if ($this.hasClass('rgb-widget-mode-fade')) {
var color = $('.rgb-widget-colorpicker-fade').spectrum('get');
var red = Math.round(color._r);
var green = Math.round(color._g);
var blue = Math.round(color._b);
send({
"type": "rgb",
"item_id": item_id,
"mode": "fade",
"fade_time": parseInt($('#rgb-widget-fade-fade-time').val()),
"color_time": parseInt($('#rgb-widget-fade-color-time').val())
});
}
});
});
var item_id = $colorPicker.data('item-id');
if (Boolean(itemValues[item_id]) != false) {
$colorPicker.spectrum('set', itemValues[item_id]);
}
$colorPicker.on("dragstop.spectrum", function (e, color) {
var red = Math.round(color._r);
var green = Math.round(color._g);
var blue = Math.round(color._b);
$('.panel-item-rgb[data-item-id="' + $(this).data('item-id') + '"]').attr('style', 'background-color: rgb(' + red + ',' + green + ',' + blue + ')')
});
});
// TODO:
// $('.rgb-colorpicker').spectrum({
// showInput: true,
// showButtons: false,
// preferredFormat: 'rgb',
// change: function (color) {
// var item_id = $(this).data('item-id');
// var red = Math.round(color._r);
// var green = Math.round(color._g);
// var blue = Math.round(color._b);
//
// var fade = ($('.fade-checkbox[data-item-id="' + item_id + '"]:checked').length > 0);
//
// send({
// 'type': 'rgb',
// 'item_id': item_id,
// 'fade': fade,
// 'red': red,
// 'green': green,
// 'blue': blue
// });
// }
// });
// $('.fade-checkbox').each(function () {
// var localStorageValue = window.localStorage.getItem('fade-checkbox-' + $(this).data('item-id'));
//
// console.log(localStorageValue);
//
// this.checked = localStorageValue != null && localStorageValue != 'false';
// });
// initWebSocket(function () {
// $('input[type="checkbox"].item-switch-checkbox').click(function (e) {
// e.preventDefault();
//
// var item_id = $(this).data('item-id');
// var action = $(this).prop('checked') ? 'turnON' : 'turnOFF';
//
// send({
// "type": action,
// "item_id": item_id
// });
// });
// Delegate click on block to checkbox
// $('.item-switch .info-box').click(function (e) {
// e.preventDefault();
//
// if ($(e.target).is('.item-switch-checkbox')) {
// return false;
// }
//
// $(this).find('.item-switch-checkbox').click();
// });
// $('.rgb-mode').click(function (e) {
// e.preventDefault();
//
// var mode = $(this).data('mode');
// var start = true;
// var item_id = $(this).parents('.item-rgb').data('item-id');
//
// if ($(this).hasClass('active')) {
// start = false
// }
//
// send({
// "type": "rgbMode",
// "item_id": item_id,
// "mode": mode,
// "start": start
// });
// });
// $('.fade-checkbox').change(function (e) {
// window.localStorage.setItem('fade-checkbox-' + $(this).data('item-id'), this.checked);
// });
// });
});