Skip to content

Commit

Permalink
Merge pull request #17 from lukiffer/feature/themes
Browse files Browse the repository at this point in the history
feature: Themes Support
  • Loading branch information
lukiffer authored Sep 30, 2018
2 parents 411643a + 2163eb3 commit 2fbb177
Show file tree
Hide file tree
Showing 26 changed files with 944 additions and 66 deletions.
6 changes: 4 additions & 2 deletions .eslintrc.yml
Original file line number Diff line number Diff line change
Expand Up @@ -6,10 +6,12 @@ env:
node: true

globals:
window: true
document: true
_: true
customElements: true
Event: true
HTMLElement: true
customElements: true
_: true

rules:
indent:
Expand Down
45 changes: 35 additions & 10 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -40,12 +40,12 @@ resources:
- url: /local/node_modules/haiku/cards/haiku-room-card.js
type: module
views:
- tab_icon: mdi:home
- title: Overview
tab_icon: mdi:home
# ...
cards:
- type: "custom:haiku-room-card"
name: Master Bedroom
class: bedroom
entities:
- group.lighting_master_bedroom
- sensor.lumi_lumiweather_022cc5ba_1_1026
Expand All @@ -66,39 +66,64 @@ you deploy the `haiku` directory.
Each room card can be configured with these options:

- `name` is the room name displayed at the bottom of the card
- `class` is the type of room that will determine the background image based on theme (any of
`bedroom`, `bedroom-alternate`, `recreation`, `living-room`, `kitchen`, or `dining-room`)
- `entities` is an array of entities or groups (defined in `groups.yaml`)
- `background_image` any valid CSS value for `background-image`
- You can specify the image from a camera feed by specifying `background-image: "url('http://hassio.local:8123/your_camera_image_feed')"`
- You can also specify a `url(...)` for a static image (you can host these externally or place them in your www folder and reference
them from there).
- You can specify other valid CSS values like gradients `background_image: "linear-gradient(to top, #cfd9df 0%, #e2ebf0 100%)"`

As mentioned above, a fully-descriptive name is more useful in a global context. If you want to customize the name of a
group or entity in Haiku, simply go to the Customization section of your config and add a custom `haiku_label` attribute
to the group or entity or edit your `customize.yaml` directly:
As mentioned above, a fully-descriptive name is more useful in a global context. If you want to customize the options for a
group or entity in Haiku, you can hold alt/option and click the tile for the entity you want to customize. This allows you to edit the
`haiku_type` and `haiku_label` custom properties.

You can also edit these properties in your `customize.yaml` directly:

```yaml
fan.ge_12730_fan_control_switch_level:
haiku_label: Ceiling Fan

switch.example_light_switch:
haiku_label: Kitchen Light Switch
haiku_type: light
```
- `haiku_label` can be any string value
- `haiku_type` should be one of:
- `light`
- `temperature`
- `humidity`
- `smoke_binary`
- `co_binary`
- `air_quality`
- `motion_binary`

## Developing and Contributing


### Development Setup

You can clone this repository and start developing with a few commands:

```bash
npm install -g gulp
npm install
export HA_SSH_PORT=22
export HA_SSH_USER=pi
export HA_SSH_HOST=example.local
gulp watch
```

The `watch` command will watch the `src/**/*` glob pattern, rebuild the package on changes, and call the `deploy.sh` script.

The deployment script makes some assumptions that you have key-based SSH authentication and you're `pi@raspberrypi` is a valid
SSH target. It also assumes the destination directory to be `/home/homeassistant/.homeassistant/www/haiku` You can customize
this script as necessary.
The deployment script makes some assumptions that you have key-based SSH authentication. It also assumes the destination
directory to be `/home/homeassistant/.homeassistant/www/haiku` You can customize this script as necessary.


### Contributing

This is a fun project for exploring Polymer and Lovelace -- one that satisfies my own personal needs for Home Assistant. PRs
are welcome, but I can't make any guarantees as to my availability for PR reviews or bug fixes. Forking and customizing for your
needs might be the quickest path.
2 changes: 1 addition & 1 deletion deploy.sh
Original file line number Diff line number Diff line change
@@ -1 +1 @@
rsync -avi ./haiku/ pi@raspberrypi:/home/homeassistant/.homeassistant/www/haiku/
rsync -aviO -e "ssh -p $HA_SSH_PORT -o StrictHostKeyChecking=no" ./haiku/ $HA_SSH_USER@$HA_SSH_HOST:/home/homeassistant/.homeassistant/www/haiku/
13 changes: 10 additions & 3 deletions gulpfile.js
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ gulp.task('lint', () => {
});

gulp.task('build', ['js'], () => {
gulp.src(['.tmp/**/*.js'])
gulp.src(['.tmp/**/*.js', '.tmp/haiku.css'])
.pipe(mergeCss())
.pipe(gulp.dest('haiku'));
});
Expand All @@ -28,8 +28,15 @@ gulp.task('js', ['sass', 'lint'], () => {
.pipe(gulp.dest('.tmp'));
});

gulp.task('sass', ['clean:dist'], () => {
return gulp.src(['src/**/*.scss'])
gulp.task('sass', ['sass:global'], () => {
return gulp.src(['src/**/*.scss', '!src/styles/global/**/*.scss'])
.pipe(sassLint())
.pipe(sass().on('error', sass.logError))
.pipe(gulp.dest('.tmp'));
});

gulp.task('sass:global', ['clean:dist'], () => {
return gulp.src(['src/styles/global/haiku.scss'])
.pipe(sassLint())
.pipe(sass().on('error', sass.logError))
.pipe(gulp.dest('.tmp'));
Expand Down
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "@haiku-ui/haiku",
"version": "0.0.1",
"version": "0.1.0",
"description": "A collection of cards and other web components for the Home Assistant Lovelace UI.",
"private": false,
"repository": {
Expand Down
89 changes: 89 additions & 0 deletions src/cards/haiku-global-config.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,89 @@
import { StorageService } from '../services/storage-service.js';
import { CustomizationService } from '../services/customization-service.js';
import '../elements/haiku-global-config-dialog.js';
import 'https://unpkg.com/[email protected]/lodash.js?module';

/**
* Haiku global config UI
*/
export class HaikuGlobalConfig extends HTMLElement {

constructor() {
super();
this.customizationService = new CustomizationService(this);
}

set hass(hass) {
this.ha = hass;

if (!this.initialized) {
this.setAttribute('style', 'margin:0;');
this.initStylesheet();
this.initTheme();
this.initConfigButton();
this.initialized = true;
}
}

initStylesheet() {
if (!document.getElementById('haiku_global_css')) {
const globalCss = document.createElement('link');
globalCss.setAttribute('id', 'haiku_global_css');
globalCss.setAttribute('href', '/local/haiku/haiku.css');
globalCss.setAttribute('rel', 'stylesheet');
document.head.appendChild(globalCss);
}
}

initTheme() {
const storageService = new StorageService();
let theme = storageService.getItem('theme');
if (!theme) {
theme = 'haiku-dark';
storageService.setItem('theme', 'haiku-dark');
}
const existingCssClasses = document.body.getAttribute('class');
if (!existingCssClasses) {
document.body.setAttribute('class', theme);
}
else {
let cssClasses = existingCssClasses.split(' ');
cssClasses = _.filter(cssClasses, (cssClass) => {
return cssClass.indexOf('haiku-') === -1;
});
document.body.setAttribute('class', `${cssClasses.join(' ')} ${theme}`);
}
}

initConfigButton() {
if (!document.getElementById('haiku_config_button')) {
const configButton = document.createElement('button');
configButton.setAttribute('class', 'haiku-config-button');
const icon = document.createElement('ha-icon');
icon.setAttribute('icon', 'mdi:settings');
configButton.appendChild(icon);
configButton.onclick = () => {
this._openGlobalConfigDialog();
};
document.body.appendChild(configButton);
}
}

_openGlobalConfigDialog() {
const $this = this;
this.customizationService.openGlobalConfigDialog(() => {
console.log('saved...');
$this.initTheme();
});
}

setConfig(config) {
this.config = config;
}

getCardSize() {
return 0;
}
}

customElements.define('haiku-global-config', HaikuGlobalConfig);
Empty file.
19 changes: 18 additions & 1 deletion src/cards/haiku-room-card.js
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ import 'https://unpkg.com/[email protected]/lodash.js?module';
import '../elements/haiku-light-menu.js';
import '../elements/haiku-sensor-tile.js';
import '../elements/haiku-fan-tile.js';
import '../elements/haiku-thermostat-tile.js';

/**
* A card that summarizes a rooms entities.
Expand All @@ -29,6 +30,7 @@ export class HaikuRoomCard extends LitElement {
<ha-card class$="haiku-room-card ${ config.class || '' }" style$="${this.getCustomBackgroundStyle()}">
<haiku-light-menu hass="${ hass }" entities="${ this.getEntitiesByDomain('light') }"></haiku-light-menu>
<div class="tiles">
${ this.renderThermostats() }
${ this.renderSensors() }
${ this.renderFans() }
</div>
Expand All @@ -42,6 +44,10 @@ export class HaikuRoomCard extends LitElement {
const states = [];
_.each(this.config.entities, (key) => {
const state = this.hass.states[key];
if (!state) {
return;
}

const d = key.split('.')[0];
const t = state.attributes.haiku_type;
if (d === domain || t === domain) {
Expand Down Expand Up @@ -69,6 +75,17 @@ export class HaikuRoomCard extends LitElement {
`;
}

renderThermostats() {
const thermostats = this.getEntitiesByDomain('climate');
return html`
${_.map(thermostats, (thermostat) => this.renderThermostat(thermostat))}
`;
}

renderThermostat(thermostat) {
return html`<haiku-thermostat-tile hass="${ this.hass }" entity="${ thermostat }"></haiku-thermostat-tile>`;
}

renderSensors() {
const sensors = this.getEntitiesByDomain('sensor');
return html`
Expand All @@ -82,7 +99,7 @@ export class HaikuRoomCard extends LitElement {

getCustomBackgroundStyle() {
if (this.config.background_image) {
return `background-image: url("${this.config.background_image}");`;
return `background-image: ${this.config.background_image};`;
}
else {
return '';
Expand Down
66 changes: 36 additions & 30 deletions src/cards/haiku-room-card.scss
Original file line number Diff line number Diff line change
Expand Up @@ -8,38 +8,18 @@
}

.haiku-room-card {
background-size: auto 100%;
background-repeat: no-repeat;
background-position: center center;
height: 30rem;
overflow-x: hidden;
overflow-y: scroll;
-webkit-overflow-scrolling: touch;
padding: 1rem 1.5rem;

&.bedroom {
background-image: url('https://mir-s3-cdn-cf.behance.net/project_modules/max_3840/a06fac31446953.5650db6e9de74.jpg');
}

&.bedroom-alternate {
background-image: url('https://st.hzcdn.com/simgs/9e1145d707fcf5b3_4-1447/modern-bedroom.jpg');
}

&.recreation {
background-image: url('https://designingidea.com/wp-content/uploads/2016/11/large-modern-game-room-with-gray-color-theme.jpg');
}

&.living-room {
background-image: url('http://irasuite.com/wp-content/uploads/2017/08/modern-living-room-chairs.jpeg');
}
background: linear-gradient(to bottom, rgba(255,255,255,0.15) 0%, rgba(0,0,0,0.45) 100%), radial-gradient(at top center, rgba(255,255,255,0.40) 0%, rgba(0,0,0,0.40) 120%) #989898;
background-blend-mode: multiply,multiply;

&.kitchen {
background-image: url('https://germankitchencenter.com/images/Nobilia_JGF%20(20).jpg');
}

&.dining-room {
background-image: url('https://www.tinydt.net/wp-content/uploads/2018/04/glamorous-at-x-in-modern-dining-room-lighting-fixtures-formal-living-light-lowes-ceiling-lights-home-depot-low-canada-decorating.jpg');
}
background-size: auto 100%;
background-repeat: no-repeat;
background-position: center center;
}

.haiku-room-card-title {
Expand All @@ -55,21 +35,47 @@
margin-bottom: 0;
}

@mixin tiles-thirds() {
width: 32%;
&:nth-child(3n+3) {
margin-right: -1px;
}
}

@mixin tiles-halves() {
width: 48%;
&:nth-child(odd) {
margin-right: -1px;
}
}

.tiles {
display: block;
margin: 0 -3px;

& > * {
display: block;
width: 32%;
float: left;
margin: 6px 3px 0;

&:first-child {
margin-left: 0;
@media only screen and (max-width: 599px) {
@include tiles-thirds();
}

@media only screen and (min-width: 600px) and (max-width: 849px) {
@include tiles-halves();
}

@media only screen and (min-width: 850px) and (max-width: 899px) {
@include tiles-thirds();
}

@media only screen and (min-width: 900px) and (max-width: 1599px) {
@include tiles-halves();
}

&:nth-child(3n+3) {
margin-right: 0;
@media only screen and (min-width: 1600px) {
@include tiles-thirds();
}
}
}
Loading

0 comments on commit 2fbb177

Please sign in to comment.