-
Notifications
You must be signed in to change notification settings - Fork 2
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
0 parents
commit a16890e
Showing
7 changed files
with
389 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,3 @@ | ||
{ | ||
"extends": "airbnb-base" | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,3 @@ | ||
node_modules | ||
package-lock.json | ||
yarn.lock |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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). |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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, | ||
}; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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); | ||
}; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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" | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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; |