Skip to content

Commit

Permalink
Initial release
Browse files Browse the repository at this point in the history
  • Loading branch information
paolotremadio committed Feb 7, 2018
0 parents commit a16890e
Show file tree
Hide file tree
Showing 7 changed files with 389 additions and 0 deletions.
3 changes: 3 additions & 0 deletions .eslintrc
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
{
"extends": "airbnb-base"
}
3 changes: 3 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
node_modules
package-lock.json
yarn.lock
59 changes: 59 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,59 @@
# Automation - Calendar

Example config.json:

```
"accessories": [
{
"accessory": "AutomationCalendar",
"name": "Test",
"latitude": 51.4825747,
"longitude": -0.0251685
}
]
```

This accessory will create a fake sensor with some custom characteristics.
Use one or more characteristic to limit the automation to a specific period of time (e.g. only during the winter or only during the day).

## Configuration

* `latitude` your home latitude (used for astronomical calculations); e.g. *-33.8567844*
* `longitude` your home longitude (used for astronomical calculations); e.g. *151.2152967*

## Characteristics exposed

### Month of the year
The current month (1 is January, 2 is February, ..., 12 is December).

### Week of the year
The week of the year, according to the Node.js locale. See [moment.js documentation](https://momentjs.com/docs/#/get-set/week/) for more info.

### Season
The current astronomical season, based on the latitude/longitude provided.

| Season | Enum Value |
| --------- | ---------- |
| Spring | 1 |
| Summer | 2 |
| Autumn | 3 |
| Winter | 4 |

### Season name
The label of the current season (it cannot be used for automation, use the **Season** characteristic instead).

### Time of the day
The time of the day, based on the latitude/longitude provided.

| Time of the day | Enum Value |
| ----------------- | ---------- |
| Morning Twilight | 1 |
| Sunrise | 2 |
| Daytime | 3 |
| Sunset | 4 |
| Evening Twilight | 5 |
| Nighttime | 6 |

### Time of the day label
The label for the current type of the day (it cannot be used for automation, use the **Time of the day** characteristic instead).
106 changes: 106 additions & 0 deletions custom-characteristics.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,106 @@
const { Characteristic } = require('hap-nodejs');

const MonthOfYearUUID = '3470e956-0bfd-11e8-ba89-0ed5f89f718b';
const MonthOfYear = function () {
const char = new Characteristic('Month of the year', MonthOfYearUUID);

char.setProps({
format: Characteristic.Formats.UINT8,
maxValue: 12,
minValue: 1,
minStep: 1,
perms: [Characteristic.Perms.READ, Characteristic.Perms.NOTIFY],
});
char.value = char.getDefaultValue();

return char;
};
MonthOfYear.UUID = MonthOfYearUUID;

const WeekOfYearUUID = '3af64fbe-0bfd-11e8-ba89-0ed5f89f718b';
const WeekOfYear = function () {
const char = new Characteristic('Week of the year', WeekOfYearUUID);

char.setProps({
format: Characteristic.Formats.UINT8,
maxValue: 52,
minValue: 1,
minStep: 1,
perms: [Characteristic.Perms.READ, Characteristic.Perms.NOTIFY],
});
char.value = char.getDefaultValue();

return char;
};
WeekOfYear.UUID = WeekOfYearUUID;

const SeasonUUID = '9469f834-0c07-11e8-ba89-0ed5f89f718b';
const Season = function () {
const char = new Characteristic('Season', SeasonUUID);

char.setProps({
format: Characteristic.Formats.UINT8,
maxValue: 4,
minValue: 1,
minStep: 1,
perms: [Characteristic.Perms.READ, Characteristic.Perms.NOTIFY],
});
char.value = char.getDefaultValue();

return char;
};
Season.UUID = SeasonUUID;

const SeasonNameUUID = '8fb61458-0c07-11e8-ba89-0ed5f89f718b';
const SeasonName = function () {
const char = new Characteristic('Season name', SeasonNameUUID);

char.setProps({
format: Characteristic.Formats.STRING,
perms: [Characteristic.Perms.READ, Characteristic.Perms.NOTIFY],
});
char.value = char.getDefaultValue();

return char;
};
SeasonName.UUID = SeasonNameUUID;

const TimeOfDayUUID = 'b6be5238-0c0a-11e8-ba89-0ed5f89f718b';
const TimeOfDay = function () {
const char = new Characteristic('Time of the day', TimeOfDayUUID);

char.setProps({
format: Characteristic.Formats.UINT8,
maxValue: 6,
minValue: 1,
minStep: 1,
perms: [Characteristic.Perms.READ, Characteristic.Perms.NOTIFY],
});
char.value = char.getDefaultValue();

return char;
};
TimeOfDay.UUID = TimeOfDayUUID;

const TimeOfDayNameUUID = 'a9d6a962-0c0a-11e8-ba89-0ed5f89f718b';
const TimeOfDayName = function () {
const char = new Characteristic('Time of the day label', TimeOfDayNameUUID);

char.setProps({
format: Characteristic.Formats.STRING,
perms: [Characteristic.Perms.READ, Characteristic.Perms.NOTIFY],
});
char.value = char.getDefaultValue();

return char;
};
TimeOfDayName.UUID = TimeOfDayNameUUID;

module.exports = {
MonthOfYear,
WeekOfYear,
Season,
SeasonName,
TimeOfDay,
TimeOfDayName,
};
133 changes: 133 additions & 0 deletions index.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,133 @@
const moment = require('moment');
const debug = require('debug')('homebridge-automation-calendar');
const seasonCalculator = require('date-season');
const CustomCharacteristics = require('./custom-characteristics');
const timeOfDayCalculator = require('./time-of-day');

let Service;

class AutomationCalendar {
constructor(log, config) {
this.log = log;
this.name = config.name;

this.latitude = config.latitude || 0.0;
this.longitude = config.longitude || 0.0;

this.hemisphere = config.latitude >= 0 ? 'north' : 'south';

debug(`Using astronomic calendar to get current season - ${this.hemisphere} hemisphere`);

this.motionService = new Service.MotionSensor(this.name);

this.motionService
.addCharacteristic(CustomCharacteristics.MonthOfYear)
.on('get', callback => callback(null, this.constructor.getMonthOfYear()));

this.motionService
.addCharacteristic(CustomCharacteristics.WeekOfYear)
.on('get', callback => callback(null, this.constructor.getWeekOfYear()));

this.motionService
.addCharacteristic(CustomCharacteristics.Season)
.on('get', callback => callback(null, this.getSeason()));

this.motionService
.addCharacteristic(CustomCharacteristics.SeasonName)
.on('get', callback => callback(null, this.getSeasonName()));

this.motionService
.addCharacteristic(CustomCharacteristics.TimeOfDay)
.on('get', callback => callback(null, this.getTimeOfDay()));

this.motionService
.addCharacteristic(CustomCharacteristics.TimeOfDayName)
.on('get', callback => callback(null, this.getTimeOfDayName()));

this.refreshValues();
}

getServices() {
return [this.motionService];
}

static getMonthOfYear() {
return (new Date()).getMonth() + 1;
}

static getWeekOfYear() {
return moment().week();
}

getSeason() {
const seasons = {
Spring: 1,
Summer: 2,
Autumn: 3,
Winter: 4,
};

return seasons[this.getSeasonName()];
}

getSeasonName() {
const seasonResolver = seasonCalculator({ north: this.hemisphere === 'north', autumn: true });

return seasonResolver(new Date());
}

getTimeOfDay() {
return timeOfDayCalculator(this.latitude, this.longitude);
}

getTimeOfDayName() {
const names = {
0: 'Night',
1: 'Morning Twilight',
2: 'Sunrise',
3: 'Daytime',
4: 'Sunset',
5: 'Evening Twilight',
};

return names[this.getTimeOfDay()];
}

refreshValues() {
this.motionService
.getCharacteristic(CustomCharacteristics.MonthOfYear)
.updateValue(this.constructor.getMonthOfYear());

this.motionService
.getCharacteristic(CustomCharacteristics.WeekOfYear)
.updateValue(this.constructor.getWeekOfYear());

this.motionService
.getCharacteristic(CustomCharacteristics.Season)
.updateValue(this.getSeason());

this.motionService
.getCharacteristic(CustomCharacteristics.SeasonName)
.updateValue(this.getSeasonName());

this.motionService
.getCharacteristic(CustomCharacteristics.TimeOfDay)
.updateValue(this.getTimeOfDay());

this.motionService
.getCharacteristic(CustomCharacteristics.TimeOfDayName)
.updateValue(this.getTimeOfDayName());

// Set timeout
setTimeout(
this.refreshValues.bind(this),
60000,
);
}
}

module.exports = (homebridge) => {
Service = homebridge.hap.Service; // eslint-disable-line

homebridge.registerAccessory('homebridge-automation-calendar', 'AutomationCalendar', AutomationCalendar);
};
38 changes: 38 additions & 0 deletions package.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
{
"name": "homebridge-automation-calendar",
"version": "0.0.1",
"description": "Useful calendar information for HomeKit automations (seasons, week of the year, month of the year, morning twilight/sunrise/daytime/sunset/evening twilight/night)",
"license": "ISC",
"keywords": [
"homebridge-plugin"
],
"author": {
"name": "Paolo Tremadio"
},
"repository": {
"type": "git",
"url": "git://github.com/paolotremadio/homebridge-automation-calendar.git"
},
"bugs": {
"url": "http://github.com/paolotremadio/homebridge-automation-calendar/issues"
},
"engines": {
"node": ">=6.0.0",
"homebridge": ">=0.4.0"
},
"dependencies": {
"date-season": "^0.0.2",
"debug": "^3.1.0",
"hap-nodejs": "^0.4.28",
"moment": "^2.20.1",
"suncalc": "^1.8.0"
},
"devDependencies": {
"eslint": "^4.16.0",
"eslint-config-airbnb-base": "^12.1.0",
"eslint-plugin-import": "^2.8.0"
},
"scripts": {
"lint": "./node_modules/eslint/bin/eslint.js . --ext .js"
}
}
47 changes: 47 additions & 0 deletions time-of-day.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
const suncalc = require('suncalc');

// Thanks to https://github.com/kcharwood/homebridge-suncalc/blob/master/index.js
const timeOfDay = (latitude, longitude) => {
const nowDate = new Date();
const now = nowDate.getTime();

const sunDates = suncalc.getTimes(
nowDate,
latitude,
longitude,
);

const times = {
dawn: sunDates.dawn.getTime(),
sunrise: sunDates.sunrise.getTime(),
sunriseEnd: sunDates.sunriseEnd.getTime() + (1000 * 60),
sunsetStart: sunDates.sunsetStart.getTime() + (1000 * 60),
sunset: sunDates.sunset.getTime(),
dusk: sunDates.dusk.getTime(),
};

if (now < times.dawn) {
// Nighttime
return 6;
} else if (now >= times.dawn && now < times.sunrise) {
// Morning Twilight
return 1;
} else if (now >= times.sunrise && now < times.sunriseEnd) {
// Sunrise
return 2;
} else if (now >= times.sunriseEnd && now < times.sunsetStart) {
// Daytime
return 3;
} else if (now >= times.sunsetStart && now < times.sunset) {
// Sunset
return 4;
} else if (now >= times.sunset && now < times.dusk) {
// Evening Twilight
return 5;
}

// Nighttime
return 6;
};

module.exports = timeOfDay;

0 comments on commit a16890e

Please sign in to comment.