Skip to content
This repository has been archived by the owner on Nov 4, 2023. It is now read-only.

fix(LIGHT): show color picker based on feature check #488

Merged
merged 3 commits into from
Oct 21, 2020
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion .eslintrc.yaml
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
env:
browser: true
es2017: true
es2020: true
node: true
parserOptions:
sourceType: module
Expand Down
8 changes: 3 additions & 5 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -356,12 +356,10 @@ Tile Object. [Click here for some real-life examples](TILE_EXAMPLES.md)
/** type: LIGHT **/
/* sliders: list of slider object. See slider documentation below */
sliders: [{}],

/* colorpicker: whether or not the color picker should be used.
* Only works with lights that have the rgb_color attribute
* Valid options: true, false
/* colorpicker: whether or not the color picker should be shown.
* Valid options: true, false, function. Default: auto-detected
*/
colorpicker: true,
colorpicker: false,
/** type: POPUP_IFRAME **/
url: String || Function,
/* optional */
Expand Down
1 change: 0 additions & 1 deletion TILE_EXAMPLES.md
Original file line number Diff line number Diff line change
Expand Up @@ -414,7 +414,6 @@ Light switch. You can optionally define sliders to control colour temperature or
}
}
],
colorpicker: true
}
```

Expand Down
2 changes: 1 addition & 1 deletion index.html.ejs
Original file line number Diff line number Diff line change
Expand Up @@ -582,7 +582,7 @@
</div>
</div>

<div ng-if="item.colorpicker" class="item-entity-colorpicker">
<div ng-if="itemField('colorpicker', item, entity)" class="item-entity-colorpicker">
<span>Color:</span>
<input color-picker
color-picker-model="getRGBStringFromArray(entity.attributes.rgb_color)"
Expand Down
44 changes: 44 additions & 0 deletions scripts/controllers/main-utilities.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
import angular from 'angular';
rchl marked this conversation as resolved.
Show resolved Hide resolved
import { TILE_DEFAULTS, TYPES } from '../globals/constants';

export function mergeConfigDefaults (pages) {
for (const page of pages) {
for (const group of page.groups) {
mergeTileListDefaults(group.items);
}
}
return pages;
}

function mergeTileListDefaults (tiles) {
if (!Array.isArray(tiles)) {
return;
}
for (const [index, tile] of tiles.entries()) {
tiles[index] = mergeTileDefaults(tile);
switch (tile.type) {
case TYPES.CAMERA:
case TYPES.CAMERA_STREAM:
case TYPES.CAMERA_THUMBNAIL:
rchl marked this conversation as resolved.
Show resolved Hide resolved
tile.fullscreen = mergeTileDefaults(tile.fullscreen);
break;
case TYPES.DOOR_ENTRY:
if (tile.layout?.camera) {
tile.layout.camera = mergeTileDefaults(tile.layout.camera);
}
mergeTileListDefaults(tile.layout?.tiles);
break;
case TYPES.POPUP:
mergeTileListDefaults(tile.popup?.items);
break;
}
}
return tiles;
}

function mergeTileDefaults (tile) {
if (tile && tile.type in TILE_DEFAULTS) {
return angular.merge({}, TILE_DEFAULTS[tile.type], tile);
}
return tile;
}
54 changes: 10 additions & 44 deletions scripts/controllers/main.js
Original file line number Diff line number Diff line change
@@ -1,8 +1,9 @@
import angular from 'angular';
import Hammer from 'hammerjs';
import { mergeConfigDefaults } from './main-utilities';
import { App } from '../app';
import { TYPES, FEATURES, HEADER_ITEMS, MENU_POSITIONS, GROUP_ALIGNS, TRANSITIONS, MAPBOX_MAP, YANDEX_MAP, DEFAULT_SLIDER_OPTIONS, DEFAULT_LIGHT_SLIDER_OPTIONS, DEFAULT_VOLUME_SLIDER_OPTIONS } from '../globals/constants';
import { debounce, leadZero, toAbsoluteServerURL } from '../globals/utils';
import { debounce, leadZero, supportsFeature, toAbsoluteServerURL } from '../globals/utils';
import Noty from '../models/noty';

App.controller('Main', function ($scope, $timeout, $location, Api) {
Expand All @@ -12,7 +13,7 @@ App.controller('Main', function ($scope, $timeout, $location, Api) {

const CONFIG = window.CONFIG;

$scope.pages = CONFIG.pages;
$scope.pages = mergeConfigDefaults(CONFIG.pages);
$scope.pagesContainerStyles = {};
$scope.TYPES = TYPES;
$scope.FEATURES = FEATURES;
Expand All @@ -34,6 +35,8 @@ App.controller('Main', function ($scope, $timeout, $location, Api) {
$scope.alarmCode = null;
$scope.activeAlarm = null;

$scope.supportsFeature = supportsFeature;

let showedPages = [];

const latestAlarmActions = {};
Expand Down Expand Up @@ -864,7 +867,7 @@ App.controller('Main', function ($scope, $timeout, $location, Api) {
};

$scope.openLightSliders = function (item, entity) {
if ((!item.sliders || !item.sliders.length) && !item.colorpicker) {
if ((!item.sliders || !item.sliders.length) && !$scope.itemField('colorpicker', item, entity)) {
return;
}

Expand Down Expand Up @@ -902,58 +905,21 @@ App.controller('Main', function ($scope, $timeout, $location, Api) {
return false;
};

$scope.supportsFeature = function (feature, entity) {
if (!('supported_features' in entity.attributes)) {
return false;
}

const features = entity.attributes.supported_features;

return (features | feature) === features;
};

$scope.shouldShowVolumeSlider = function (entity) {
return $scope.supportsFeature(FEATURES.MEDIA_PLAYER.VOLUME_SET, entity)
return supportsFeature(FEATURES.MEDIA_PLAYER.VOLUME_SET, entity)
&& ('volume_level' in entity.attributes)
&& entity.state !== 'off';
};

$scope.shouldShowVolumeButtons = function (entity) {
return (!$scope.supportsFeature(FEATURES.MEDIA_PLAYER.VOLUME_SET, entity)
return (!supportsFeature(FEATURES.MEDIA_PLAYER.VOLUME_SET, entity)
|| !('volume_level' in entity.attributes))
&& $scope.supportsFeature(FEATURES.MEDIA_PLAYER.VOLUME_STEP, entity)
&& supportsFeature(FEATURES.MEDIA_PLAYER.VOLUME_STEP, entity)
&& entity.state !== 'off';
};

const GAUGE_DEFAULTS = {
backgroundColor: 'rgba(0, 0, 0, 0.1)',
foregroundColor: 'rgba(0, 150, 136, 1)',
size: function (item) {
return .8 * (CONFIG.tileSize * (item.height < item.width ? item.height : item.width));
},
duration: 1500,
thick: 6,
type: 'full',
min: 0,
max: 100,
cap: 'butt',
thresholds: {},
};

$scope.getGaugeField = function (field, item, entity) {
if (!item) {
return null;
}

if (item.settings && field in item.settings) {
return parseFieldValue(item.settings[field], item, entity);
}

if (field in GAUGE_DEFAULTS) {
return parseFieldValue(GAUGE_DEFAULTS[field], item, entity);
}

return null;
return getItemFieldValue(`settings.${field}`, item, entity);
};

$scope.itemURL = function (item, entity) {
Expand Down
31 changes: 30 additions & 1 deletion scripts/globals/constants.js
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { timeAgo } from './utils';
import { supportsFeature, timeAgo } from './utils';

export const CLASS_BIG = '-big-entity';
export const CLASS_SMALL = '-small-entity';
Expand Down Expand Up @@ -87,7 +87,14 @@ export const FEATURES = {
ARM_CUSTOM_BYPASS: 16,
},
LIGHT: {
// https://github.com/home-assistant/core/blob/dev/homeassistant/components/light/__init__.py
BRIGHTNESS: 1,
COLOR_TEMP: 2,
EFFECT: 4,
FLASH: 8,
COLOR: 16,
TRANSITION: 32,
WHITE_VALUE: 128,
},
MEDIA_PLAYER: {
PAUSE: 1,
Expand Down Expand Up @@ -269,3 +276,25 @@ export const DEFAULT_VOLUME_SLIDER_OPTIONS = {
field: 'volume_level',
},
};

export const TILE_DEFAULTS = {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Should we explain somewhere in the docs that the following will now be possible in config.js?

TILE_DEFAULTS[TYPES.LIGHT].colorpicker = false;

Maybe be even add a line to the example:

TILE_DEFAULTS = angular.merge(TILE_DEFAULTS, {
   // override defaults here, e.g.:
   [TYPES.LIGHT]: {
      colorpicker: false
   }
});

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We should but I haven't figured out a way to state that in a generic way. Maybe we should wait until we have all tiles covered that way because now the instructions would have to be rather specific.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Also fine with me, that would give us time to sort out last issues with this new scheme before people start to use it. I'll certainly help find those issues, because I'll switch my whole setup to use the defaults.

We just shouldn't forget...

[TYPES.GAUGE]: {
settings: {
backgroundColor: 'rgba(0, 0, 0, 0.1)',
foregroundColor: 'rgba(0, 150, 136, 1)',
size (item) {
rchl marked this conversation as resolved.
Show resolved Hide resolved
return .8 * (window.CONFIG.tileSize * (item.height < item.width ? item.height : item.width));
},
duration: 1500,
thick: 6,
type: 'full',
min: 0,
max: 100,
cap: 'butt',
thresholds: {},
},
},
[TYPES.LIGHT]: {
colorpicker: (item, entity) => supportsFeature(FEATURES.LIGHT.COLOR, entity),
rchl marked this conversation as resolved.
Show resolved Hide resolved
},
};
5 changes: 5 additions & 0 deletions scripts/globals/utils.js
Original file line number Diff line number Diff line change
Expand Up @@ -129,3 +129,8 @@ export const toAbsoluteServerURL = function (path) {
// Replace extra forward slashes but not in protocol.
return url.replace(/([^:])\/+/g, '$1/');
};

export function supportsFeature (feature, entity) {
return 'supported_features' in entity.attributes
&& (entity.attributes.supported_features & feature) !== 0;
}