homebridge-eggtimer-plugin 1.0.30
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.github/FUNDING.yml +3 -0
- package/.github/dependabot.yml +19 -0
- package/.github/workflows/audit.yml +30 -0
- package/.github/workflows/auto-merge-dependabot.yml +16 -0
- package/.github/workflows/build.yml +32 -0
- package/.github/workflows/corepack.yml +44 -0
- package/.github/workflows/npm-publish.yml +57 -0
- package/README.md +44 -0
- package/SECURITY.md +11 -0
- package/config.schema.json +37 -0
- package/dist/accessory.d.ts +4 -0
- package/dist/accessory.d.ts.map +1 -0
- package/dist/accessory.js +134 -0
- package/dist/accessory.js.map +1 -0
- package/eslint.config.js +36 -0
- package/package.json +57 -0
- package/src/accessory.ts +182 -0
- package/tsconfig.json +23 -0
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
# To get started with Dependabot version updates, you'll need to specify which
|
|
2
|
+
# package ecosystems to update and where the package manifests are located.
|
|
3
|
+
# Please see the documentation for all configuration options:
|
|
4
|
+
# https://help.github.com/github/administering-a-repository/configuration-options-for-dependency-updates
|
|
5
|
+
|
|
6
|
+
version: 2
|
|
7
|
+
updates:
|
|
8
|
+
|
|
9
|
+
# Maintain dependencies for GitHub Actions
|
|
10
|
+
- package-ecosystem: "github-actions"
|
|
11
|
+
directory: "/"
|
|
12
|
+
schedule:
|
|
13
|
+
interval: "weekly"
|
|
14
|
+
|
|
15
|
+
# Maintain dependencies for npm
|
|
16
|
+
- package-ecosystem: "pnpm"
|
|
17
|
+
directory: "/"
|
|
18
|
+
schedule:
|
|
19
|
+
interval: "weekly"
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
# This workflow will do a clean install of node dependencies, build the source code and run tests across different versions of node
|
|
2
|
+
# For more information see: https://help.github.com/actions/language-and-framework-guides/using-nodejs-with-github-actions
|
|
3
|
+
|
|
4
|
+
name: Audit
|
|
5
|
+
|
|
6
|
+
on:
|
|
7
|
+
|
|
8
|
+
schedule:
|
|
9
|
+
- cron: '0 0 * * *'
|
|
10
|
+
workflow_dispatch:
|
|
11
|
+
|
|
12
|
+
jobs:
|
|
13
|
+
build:
|
|
14
|
+
|
|
15
|
+
runs-on: ubuntu-latest
|
|
16
|
+
|
|
17
|
+
steps:
|
|
18
|
+
- uses: actions/checkout@v4
|
|
19
|
+
- uses: actions/cache@v4
|
|
20
|
+
name: Cache node modules
|
|
21
|
+
with:
|
|
22
|
+
path: node_modules
|
|
23
|
+
key: ${{ runner.os }}-node-${{ hashFiles('**/pnpm-lock.yaml') }}
|
|
24
|
+
- name: Use Node.js 20.x
|
|
25
|
+
uses: actions/setup-node@v4.0.2
|
|
26
|
+
with:
|
|
27
|
+
node-version: 20.x
|
|
28
|
+
- uses: pnpm/action-setup@v3
|
|
29
|
+
- run: pnpm install --frozen-lockfile
|
|
30
|
+
- run: pnpm audit
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
name: Auto-merge Dependabot
|
|
2
|
+
on: pull_request
|
|
3
|
+
|
|
4
|
+
permissions:
|
|
5
|
+
pull-requests: write
|
|
6
|
+
contents: write
|
|
7
|
+
|
|
8
|
+
jobs:
|
|
9
|
+
automerge:
|
|
10
|
+
runs-on: ubuntu-latest
|
|
11
|
+
if: github.actor == 'dependabot[bot]' || (startsWith(github.event.pull_request.title, 'Bump to version ') && (github.actor == 'github-actions' || github.actor == 'teh-hippo'))
|
|
12
|
+
steps:
|
|
13
|
+
- uses: peter-evans/enable-pull-request-automerge@v3
|
|
14
|
+
with:
|
|
15
|
+
pull-request-number: ${{ github.event.pull_request.number }}
|
|
16
|
+
merge-method: rebase
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
# This workflow will do a clean install of node dependencies, build the source code and run tests across different versions of node
|
|
2
|
+
# For more information see: https://help.github.com/actions/language-and-framework-guides/using-nodejs-with-github-actions
|
|
3
|
+
|
|
4
|
+
name: Build
|
|
5
|
+
|
|
6
|
+
on:
|
|
7
|
+
pull_request:
|
|
8
|
+
|
|
9
|
+
jobs:
|
|
10
|
+
|
|
11
|
+
build:
|
|
12
|
+
|
|
13
|
+
runs-on: ubuntu-latest
|
|
14
|
+
|
|
15
|
+
strategy:
|
|
16
|
+
matrix:
|
|
17
|
+
node-version: [16.x, 18.x, 20.x]
|
|
18
|
+
|
|
19
|
+
steps:
|
|
20
|
+
- uses: actions/checkout@v4
|
|
21
|
+
- uses: actions/cache@v4
|
|
22
|
+
name: Cache node modules
|
|
23
|
+
with:
|
|
24
|
+
path: node_modules
|
|
25
|
+
key: ${{ runner.os }}-node-${{ hashFiles('**/pnpm-lock.yaml') }}
|
|
26
|
+
- name: Use Node.js ${{ matrix.node-version }}
|
|
27
|
+
uses: actions/setup-node@v4.0.2
|
|
28
|
+
with:
|
|
29
|
+
node-version: ${{ matrix.node-version }}
|
|
30
|
+
- uses: pnpm/action-setup@v3
|
|
31
|
+
- run: pnpm install --frozen-lockfile
|
|
32
|
+
- run: pnpm lint
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
# This workflow will do a clean install of node dependencies, build the source code and run tests across different versions of node
|
|
2
|
+
# For more information see: https://help.github.com/actions/language-and-framework-guides/using-nodejs-with-github-actions
|
|
3
|
+
|
|
4
|
+
name: Update PNPM
|
|
5
|
+
|
|
6
|
+
on:
|
|
7
|
+
|
|
8
|
+
schedule:
|
|
9
|
+
- cron: '0 0 * * *'
|
|
10
|
+
workflow_dispatch:
|
|
11
|
+
|
|
12
|
+
permissions:
|
|
13
|
+
contents: write
|
|
14
|
+
pull-requests: write
|
|
15
|
+
|
|
16
|
+
jobs:
|
|
17
|
+
build:
|
|
18
|
+
|
|
19
|
+
runs-on: ubuntu-latest
|
|
20
|
+
|
|
21
|
+
steps:
|
|
22
|
+
- uses: actions/checkout@v4
|
|
23
|
+
- uses: actions/cache@v4
|
|
24
|
+
name: Cache node modules
|
|
25
|
+
with:
|
|
26
|
+
path: node_modules
|
|
27
|
+
key: ${{ runner.os }}-node-${{ hashFiles('**/pnpm-lock.json') }}
|
|
28
|
+
- name: Use Node.js 20.x
|
|
29
|
+
uses: actions/setup-node@v4.0.2
|
|
30
|
+
with:
|
|
31
|
+
node-version: 20.x
|
|
32
|
+
- uses: pnpm/action-setup@v3
|
|
33
|
+
- run: corepack up
|
|
34
|
+
- name: Create Pull Request
|
|
35
|
+
uses: peter-evans/create-pull-request@v6
|
|
36
|
+
with:
|
|
37
|
+
reviewers: teh-hippo
|
|
38
|
+
delete-branch: true
|
|
39
|
+
title: Update PNPM
|
|
40
|
+
commit-message: |
|
|
41
|
+
Update to the latest PNPM manager.
|
|
42
|
+
token: ${{ secrets.GITHUB_TOKEN }}
|
|
43
|
+
committer: AutoHippo <auto@hippo.org>
|
|
44
|
+
branch: autohippo/update-pnpm-${{ github.run_number }}
|
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
# This workflow will run tests using node and then publish a package to GitHub Packages when a release is created
|
|
2
|
+
# For more information see: https://help.github.com/actions/language-and-framework-guides/publishing-nodejs-packages
|
|
3
|
+
|
|
4
|
+
name: Release
|
|
5
|
+
|
|
6
|
+
on:
|
|
7
|
+
release:
|
|
8
|
+
types: [published]
|
|
9
|
+
|
|
10
|
+
jobs:
|
|
11
|
+
publish:
|
|
12
|
+
runs-on: ubuntu-latest
|
|
13
|
+
steps:
|
|
14
|
+
- uses: actions/checkout@v4
|
|
15
|
+
- uses: actions/setup-node@v4.0.2
|
|
16
|
+
with:
|
|
17
|
+
node-version: 18
|
|
18
|
+
registry-url: https://registry.npmjs.org/
|
|
19
|
+
- uses: pnpm/action-setup@v3
|
|
20
|
+
- run: pnpm install --frozen-lockfile
|
|
21
|
+
- run: pnpm build
|
|
22
|
+
- run: pnpm publish --no-git-checks
|
|
23
|
+
env:
|
|
24
|
+
NODE_AUTH_TOKEN: ${{ secrets.NPM_TOKEN }}
|
|
25
|
+
|
|
26
|
+
update-build-number:
|
|
27
|
+
needs: publish
|
|
28
|
+
runs-on: ubuntu-latest
|
|
29
|
+
permissions:
|
|
30
|
+
pull-requests: write
|
|
31
|
+
contents: write
|
|
32
|
+
steps:
|
|
33
|
+
- uses: actions/checkout@v4
|
|
34
|
+
with:
|
|
35
|
+
ref: main
|
|
36
|
+
fetch-tags: true
|
|
37
|
+
- uses: actions/setup-node@v4.0.2
|
|
38
|
+
with:
|
|
39
|
+
node-version: 18
|
|
40
|
+
registry-url: https://registry.npmjs.org/
|
|
41
|
+
- uses: pnpm/action-setup@v3
|
|
42
|
+
- run: pnpm version patch --no-git-tag-version
|
|
43
|
+
- name: get-npm-version
|
|
44
|
+
id: version-after
|
|
45
|
+
uses: martinbeentjes/npm-get-version-action@v1.3.1
|
|
46
|
+
- name: Create Pull Request
|
|
47
|
+
uses: peter-evans/create-pull-request@v6
|
|
48
|
+
with:
|
|
49
|
+
add-paths: |
|
|
50
|
+
package.json
|
|
51
|
+
delete-branch: true
|
|
52
|
+
title: Bump to version ${{ steps.version-after.outputs.current-version }}
|
|
53
|
+
commit-message: |
|
|
54
|
+
Bump to version ${{ steps.version-after.outputs.current-version }}
|
|
55
|
+
token: ${{ secrets.PAT }}
|
|
56
|
+
committer: AutoHippo <auto@hippo.org>
|
|
57
|
+
branch: bump-to-version-${{ steps.version-after.outputs.current-version }}
|
package/README.md
ADDED
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+

|
|
2
|
+
[](https://www.npmjs.com/package/homebridge-eggtimer-plugin)
|
|
3
|
+
[](https://badge.fury.io/js/homebridge-eggtimer-plugin)
|
|
4
|
+

|
|
5
|
+
|
|
6
|
+
[](https://github.com/homebridge/homebridge/wiki/Verified-Plugins)
|
|
7
|
+
|
|
8
|
+
# Homebridge Egg Timer Plugin
|
|
9
|
+
|
|
10
|
+
Example config.json:
|
|
11
|
+
|
|
12
|
+
```json
|
|
13
|
+
"accessories": [
|
|
14
|
+
{
|
|
15
|
+
"name": "Timer Bulb 1",
|
|
16
|
+
"interval": 60000,
|
|
17
|
+
"stateful": false,
|
|
18
|
+
"occupancySensor": false,
|
|
19
|
+
"accessory": "EggTimerBulb",
|
|
20
|
+
},
|
|
21
|
+
]
|
|
22
|
+
```
|
|
23
|
+
|
|
24
|
+
This plugin will create a fake bulb that provides an everyday egg-timer for automations.
|
|
25
|
+
Brightness is used as an indicator of the time remaining and can be adjusted after the time has started.
|
|
26
|
+
When turned, the brightness will be decremented at set intervals (per minute by default).
|
|
27
|
+
HomeKit automations can be triggered based on the timer commencing (bulb on) and on completion (bulb off).
|
|
28
|
+
|
|
29
|
+
Unlike [homebridge-delay-switch](https://github.com/nitaybz/homebridge-delay-switch), the timer length is provided each time - similar to a real-world egg timer.
|
|
30
|
+
|
|
31
|
+
This was created originally created to help with kids' bedtime routines, where nightlights would be turned off automatically after an allowed amount of reading time (call me crazy, its 2023).
|
|
32
|
+
|
|
33
|
+
Other example usages found since:
|
|
34
|
+
|
|
35
|
+
* Automating a desk heater for bursts of heat in the winter.
|
|
36
|
+
* Running our air-con for a while and having it turn off automatically.
|
|
37
|
+
|
|
38
|
+
## Parameters
|
|
39
|
+
|
|
40
|
+
| Parameter | Description | Default |
|
|
41
|
+
| --------- | ----- | ------- |
|
|
42
|
+
| `interval`| How often to decrement the brightness. | `60000` (1 minute) |
|
|
43
|
+
| `stateful`| Persist the timer state between restarts. | `false` |
|
|
44
|
+
| `occupancySensor`| Add an occupancy sensor that reflects the timer's current state. | `false` |
|
package/SECURITY.md
ADDED
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
{
|
|
2
|
+
"pluginAlias": "EggTimerBulb",
|
|
3
|
+
"pluginType": "accessory",
|
|
4
|
+
"singular": false,
|
|
5
|
+
"headerDisplay": "Everyday egg timers for HomeBridge",
|
|
6
|
+
"footerDisplay": "Created by teh-hippo",
|
|
7
|
+
"schema": {
|
|
8
|
+
"type": "object",
|
|
9
|
+
"properties": {
|
|
10
|
+
"name": {
|
|
11
|
+
"title": "Accessory Name",
|
|
12
|
+
"description": "Name for the accessory",
|
|
13
|
+
"type": "string",
|
|
14
|
+
"required": true
|
|
15
|
+
},
|
|
16
|
+
"interval": {
|
|
17
|
+
"title": "Interval Time in Milliseconds",
|
|
18
|
+
"description": "Amount of time in milliseconds to wait in between each decrement of the brightness",
|
|
19
|
+
"type": "integer",
|
|
20
|
+
"default": 60000,
|
|
21
|
+
"required": true
|
|
22
|
+
},
|
|
23
|
+
"stateful": {
|
|
24
|
+
"title": "Stateful",
|
|
25
|
+
"description": "Persist the timer state between restarts.",
|
|
26
|
+
"type": "boolean",
|
|
27
|
+
"required": false
|
|
28
|
+
},
|
|
29
|
+
"occupancySensor": {
|
|
30
|
+
"title": "Occupancy Sensor",
|
|
31
|
+
"description": "Add an occupancy sensor that reflects the timer's current state",
|
|
32
|
+
"type": "boolean",
|
|
33
|
+
"required": false
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
}
|
|
37
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"accessory.d.ts","sourceRoot":"","sources":["../src/accessory.ts"],"names":[],"mappings":"AAAA,OAAO,EAGL,GAAG,EAKJ,MAAM,YAAY,CAAC;8BA2KL,GAAG;AAAlB,kBAEE"}
|
|
@@ -0,0 +1,134 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
3
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
4
|
+
};
|
|
5
|
+
const node_persist_1 = require("node-persist");
|
|
6
|
+
const async_lock_1 = __importDefault(require("async-lock"));
|
|
7
|
+
class EggTimerBulb {
|
|
8
|
+
constructor(log, config, api) {
|
|
9
|
+
this.brightness = 0;
|
|
10
|
+
this.log = log;
|
|
11
|
+
this.hap = api.hap;
|
|
12
|
+
this.interval = Number(config.interval);
|
|
13
|
+
this.stateRestored = false;
|
|
14
|
+
this.lightbulbService = new this.hap.Service.Lightbulb(config.name);
|
|
15
|
+
this.lightbulbService.getCharacteristic(this.hap.Characteristic.On)
|
|
16
|
+
.onGet(this.getOn.bind(this))
|
|
17
|
+
.onSet(this.setOn.bind(this));
|
|
18
|
+
this.lightbulbService.getCharacteristic(this.hap.Characteristic.Brightness)
|
|
19
|
+
.onGet(this.getBrightness.bind(this))
|
|
20
|
+
.onSet(this.setBrightness.bind(this));
|
|
21
|
+
this.informationService = new this.hap.Service.AccessoryInformation()
|
|
22
|
+
.setCharacteristic(this.hap.Characteristic.Manufacturer, "Egg Timer Bulb")
|
|
23
|
+
.setCharacteristic(this.hap.Characteristic.Model, `${config.name} (${this.interval.toString()}ms)`);
|
|
24
|
+
this.stateful = config.stateful === true;
|
|
25
|
+
this.storageKey = `${config.name}-${this.interval.toString()}`;
|
|
26
|
+
this.storageDir = api.user.persistPath();
|
|
27
|
+
this.lock = new async_lock_1.default();
|
|
28
|
+
if (config.occupancySensor === true) {
|
|
29
|
+
this.occupancyService = new this.hap.Service.OccupancySensor(`${config.name} Active`);
|
|
30
|
+
this.occupancyService.getCharacteristic(this.hap.Characteristic.OccupancyDetected)
|
|
31
|
+
.onGet(this.getOccupancy.bind(this));
|
|
32
|
+
}
|
|
33
|
+
if (this.stateful) {
|
|
34
|
+
this.restoreState().catch((error) => {
|
|
35
|
+
this.log.error(String(error));
|
|
36
|
+
});
|
|
37
|
+
}
|
|
38
|
+
}
|
|
39
|
+
getServices() {
|
|
40
|
+
const services = [
|
|
41
|
+
this.informationService,
|
|
42
|
+
this.lightbulbService
|
|
43
|
+
];
|
|
44
|
+
if (this.occupancyService) {
|
|
45
|
+
services.push(this.occupancyService);
|
|
46
|
+
}
|
|
47
|
+
return services;
|
|
48
|
+
}
|
|
49
|
+
async getOn() {
|
|
50
|
+
await this.restoreState();
|
|
51
|
+
return this.brightness > 0;
|
|
52
|
+
}
|
|
53
|
+
async setOn(value) {
|
|
54
|
+
if (!value) {
|
|
55
|
+
this.log.info("Manually stopping timer.");
|
|
56
|
+
await this.updateBrightness(0);
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
async getBrightness() {
|
|
60
|
+
await this.restoreState();
|
|
61
|
+
return this.brightness;
|
|
62
|
+
}
|
|
63
|
+
async setBrightness(value) {
|
|
64
|
+
const brightness = value;
|
|
65
|
+
await this.updateBrightness(brightness);
|
|
66
|
+
}
|
|
67
|
+
async getOccupancy() {
|
|
68
|
+
await this.restoreState();
|
|
69
|
+
return this.brightness > 0;
|
|
70
|
+
}
|
|
71
|
+
async updateBrightness(value) {
|
|
72
|
+
this.log.debug(`Brightness: ${this.brightness.toString()} -> ${value.toString()}`);
|
|
73
|
+
this.brightness = Math.max(0, Math.min(100, value));
|
|
74
|
+
// Persist state
|
|
75
|
+
if (this.stateful) {
|
|
76
|
+
if (this.brightness > 0) {
|
|
77
|
+
this.log.debug("Caching state");
|
|
78
|
+
await (0, node_persist_1.set)(this.storageKey, this.brightness);
|
|
79
|
+
}
|
|
80
|
+
else {
|
|
81
|
+
this.log.debug("Deleting state");
|
|
82
|
+
await (0, node_persist_1.del)(this.storageKey);
|
|
83
|
+
}
|
|
84
|
+
}
|
|
85
|
+
// Update HomeKit
|
|
86
|
+
const isActive = this.brightness > 0;
|
|
87
|
+
this.lightbulbService.updateCharacteristic(this.hap.Characteristic.Brightness, this.brightness);
|
|
88
|
+
this.lightbulbService.updateCharacteristic(this.hap.Characteristic.On, isActive);
|
|
89
|
+
if (this.occupancyService !== undefined) {
|
|
90
|
+
this.occupancyService.updateCharacteristic(this.hap.Characteristic.OccupancyDetected, isActive);
|
|
91
|
+
}
|
|
92
|
+
// Update Timer
|
|
93
|
+
if (isActive && this.timer === undefined) {
|
|
94
|
+
this.log.info("Starting timer");
|
|
95
|
+
this.timer = setInterval(() => {
|
|
96
|
+
this.updateBrightness(this.brightness - 1).catch((error) => {
|
|
97
|
+
this.log.error(String(error));
|
|
98
|
+
});
|
|
99
|
+
}, this.interval);
|
|
100
|
+
}
|
|
101
|
+
else if (this.brightness === 0) {
|
|
102
|
+
this.log.info("Timer completed.");
|
|
103
|
+
clearInterval(this.timer);
|
|
104
|
+
this.timer = undefined;
|
|
105
|
+
}
|
|
106
|
+
}
|
|
107
|
+
async restoreState() {
|
|
108
|
+
if (!this.stateful || this.stateRestored) {
|
|
109
|
+
return;
|
|
110
|
+
}
|
|
111
|
+
await this.lock.acquire("restoreState", async () => {
|
|
112
|
+
// Double-lock
|
|
113
|
+
if (this.stateRestored) {
|
|
114
|
+
return;
|
|
115
|
+
}
|
|
116
|
+
this.log.debug("Checking for stored state.");
|
|
117
|
+
await (0, node_persist_1.init)({
|
|
118
|
+
expiredInterval: 1000 * 60 * 60 * 24 * 14, // Delete cached items after 14 days.
|
|
119
|
+
forgiveParseErrors: true,
|
|
120
|
+
dir: this.storageDir
|
|
121
|
+
});
|
|
122
|
+
const value = await (0, node_persist_1.get)(this.storageKey);
|
|
123
|
+
if (value !== undefined && value > 0) {
|
|
124
|
+
this.log.info(`Restoring state to: ${value.toString()}`);
|
|
125
|
+
await this.updateBrightness(value);
|
|
126
|
+
}
|
|
127
|
+
this.stateRestored = true;
|
|
128
|
+
});
|
|
129
|
+
}
|
|
130
|
+
}
|
|
131
|
+
module.exports = (api) => {
|
|
132
|
+
api.registerAccessory("EggTimerBulb", EggTimerBulb);
|
|
133
|
+
};
|
|
134
|
+
//# sourceMappingURL=accessory.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"accessory.js","sourceRoot":"","sources":["../src/accessory.ts"],"names":[],"mappings":"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"}
|
package/eslint.config.js
ADDED
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
/* eslint-disable @typescript-eslint/no-unsafe-argument */
|
|
2
|
+
// @ts-check
|
|
3
|
+
|
|
4
|
+
//import eslint from "@eslint/js";
|
|
5
|
+
const eslint = require('@eslint/js')
|
|
6
|
+
//import tseslint from "typescript-eslint";
|
|
7
|
+
const tseslint = require('typescript-eslint')
|
|
8
|
+
//import stylistic from "@stylistic/eslint-plugin";
|
|
9
|
+
const stylistic = require('@stylistic/eslint-plugin')
|
|
10
|
+
|
|
11
|
+
module.exports = [
|
|
12
|
+
...tseslint.config(
|
|
13
|
+
// eslint-disable-next-line @typescript-eslint/no-unsafe-member-access
|
|
14
|
+
eslint.configs.recommended,
|
|
15
|
+
...tseslint.configs.stylisticTypeChecked,
|
|
16
|
+
...tseslint.configs.strictTypeChecked,
|
|
17
|
+
{
|
|
18
|
+
languageOptions: {
|
|
19
|
+
parserOptions: {
|
|
20
|
+
project: true
|
|
21
|
+
}
|
|
22
|
+
}
|
|
23
|
+
}
|
|
24
|
+
),
|
|
25
|
+
stylistic.configs.customize({
|
|
26
|
+
indent: 2,
|
|
27
|
+
quotes: "double",
|
|
28
|
+
commaDangle: "never",
|
|
29
|
+
quoteProps: "as-needed",
|
|
30
|
+
arrowParens: false,
|
|
31
|
+
blockSpacing: true,
|
|
32
|
+
braceStyle: "1tbs",
|
|
33
|
+
flat: true,
|
|
34
|
+
semi: true
|
|
35
|
+
})
|
|
36
|
+
];
|
package/package.json
ADDED
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "homebridge-eggtimer-plugin",
|
|
3
|
+
"displayName": "Homebridge Eggtimer Plugin",
|
|
4
|
+
"version": "1.0.30",
|
|
5
|
+
"description": "Egg Timers for Homebridge: https://github.com/nfarina/homebridge",
|
|
6
|
+
"license": "MIT",
|
|
7
|
+
"keywords": [
|
|
8
|
+
"homebridge-plugin",
|
|
9
|
+
"egg-timer",
|
|
10
|
+
"eggtimer",
|
|
11
|
+
"timer",
|
|
12
|
+
"countdown",
|
|
13
|
+
"delay",
|
|
14
|
+
"automation",
|
|
15
|
+
"homebridge",
|
|
16
|
+
"persistent"
|
|
17
|
+
],
|
|
18
|
+
"repository": {
|
|
19
|
+
"type": "git",
|
|
20
|
+
"url": "git://github.com/teh-hippo/homebridge-eggtimer-plugin.git"
|
|
21
|
+
},
|
|
22
|
+
"bugs": {
|
|
23
|
+
"url": "http://github.com/teh-hippo/homebridge-eggtimer-plugin/issues"
|
|
24
|
+
},
|
|
25
|
+
"funding": {
|
|
26
|
+
"type": "github",
|
|
27
|
+
"url": "https://github.com/sponsors/teh-hippo"
|
|
28
|
+
},
|
|
29
|
+
"main": "dist/accessory.js",
|
|
30
|
+
"engines": {
|
|
31
|
+
"node": ">=16.0.0",
|
|
32
|
+
"homebridge": ">=1.3.5"
|
|
33
|
+
},
|
|
34
|
+
"devDependencies": {
|
|
35
|
+
"@stylistic/eslint-plugin": "^1.8.0",
|
|
36
|
+
"@types/async-lock": "^1.4.2",
|
|
37
|
+
"@types/node": "^20.12.8",
|
|
38
|
+
"@types/node-persist": "^3.1.8",
|
|
39
|
+
"eslint": "^8.57.0",
|
|
40
|
+
"homebridge": "^1.8.1",
|
|
41
|
+
"rimraf": "^5.0.5",
|
|
42
|
+
"ts-node": "^10.9.2",
|
|
43
|
+
"typescript": "^5.4.5",
|
|
44
|
+
"typescript-eslint": "^7.8.0"
|
|
45
|
+
},
|
|
46
|
+
"dependencies": {
|
|
47
|
+
"async-lock": "^1.4.1",
|
|
48
|
+
"node-persist": "^4.0.1"
|
|
49
|
+
},
|
|
50
|
+
"packageManager": "pnpm@8.15.8+sha256.691fe176eea9a8a80df20e4976f3dfb44a04841ceb885638fe2a26174f81e65e",
|
|
51
|
+
"scripts": {
|
|
52
|
+
"lint": "eslint src",
|
|
53
|
+
"lintAndFix": "eslint --fix src",
|
|
54
|
+
"debug": "tsc && homebridge -I -D",
|
|
55
|
+
"build": "rimraf ./dist && tsc"
|
|
56
|
+
}
|
|
57
|
+
}
|
package/src/accessory.ts
ADDED
|
@@ -0,0 +1,182 @@
|
|
|
1
|
+
import {
|
|
2
|
+
AccessoryConfig,
|
|
3
|
+
AccessoryPlugin,
|
|
4
|
+
API,
|
|
5
|
+
CharacteristicValue,
|
|
6
|
+
HAP,
|
|
7
|
+
Logging,
|
|
8
|
+
Service
|
|
9
|
+
} from "homebridge";
|
|
10
|
+
|
|
11
|
+
import {
|
|
12
|
+
init,
|
|
13
|
+
set,
|
|
14
|
+
get,
|
|
15
|
+
del
|
|
16
|
+
} from "node-persist";
|
|
17
|
+
|
|
18
|
+
import AsyncLock from "async-lock";
|
|
19
|
+
|
|
20
|
+
class EggTimerBulb implements AccessoryPlugin {
|
|
21
|
+
private readonly log: Logging;
|
|
22
|
+
private readonly lightbulbService: Service;
|
|
23
|
+
private readonly informationService: Service;
|
|
24
|
+
private readonly occupancyService: Service | undefined;
|
|
25
|
+
private readonly interval: number;
|
|
26
|
+
private readonly hap: HAP;
|
|
27
|
+
private readonly storageKey: string;
|
|
28
|
+
private readonly storageDir: string;
|
|
29
|
+
private readonly stateful: boolean;
|
|
30
|
+
private readonly lock: AsyncLock;
|
|
31
|
+
private brightness = 0;
|
|
32
|
+
private timer: NodeJS.Timeout | undefined;
|
|
33
|
+
private stateRestored: boolean;
|
|
34
|
+
|
|
35
|
+
constructor(log: Logging, config: AccessoryConfig, api: API) {
|
|
36
|
+
this.log = log;
|
|
37
|
+
this.hap = api.hap;
|
|
38
|
+
this.interval = Number(config.interval);
|
|
39
|
+
this.stateRestored = false;
|
|
40
|
+
|
|
41
|
+
this.lightbulbService = new this.hap.Service.Lightbulb(config.name);
|
|
42
|
+
|
|
43
|
+
this.lightbulbService.getCharacteristic(this.hap.Characteristic.On)
|
|
44
|
+
.onGet(this.getOn.bind(this))
|
|
45
|
+
.onSet(this.setOn.bind(this));
|
|
46
|
+
|
|
47
|
+
this.lightbulbService.getCharacteristic(this.hap.Characteristic.Brightness)
|
|
48
|
+
.onGet(this.getBrightness.bind(this))
|
|
49
|
+
.onSet(this.setBrightness.bind(this));
|
|
50
|
+
|
|
51
|
+
this.informationService = new this.hap.Service.AccessoryInformation()
|
|
52
|
+
.setCharacteristic(this.hap.Characteristic.Manufacturer, "Egg Timer Bulb")
|
|
53
|
+
.setCharacteristic(this.hap.Characteristic.Model, `${config.name} (${this.interval.toString()}ms)`);
|
|
54
|
+
|
|
55
|
+
this.stateful = config.stateful === true;
|
|
56
|
+
this.storageKey = `${config.name}-${this.interval.toString()}`;
|
|
57
|
+
this.storageDir = api.user.persistPath();
|
|
58
|
+
this.lock = new AsyncLock();
|
|
59
|
+
|
|
60
|
+
if (config.occupancySensor === true) {
|
|
61
|
+
this.occupancyService = new this.hap.Service.OccupancySensor(`${config.name} Active`);
|
|
62
|
+
this.occupancyService.getCharacteristic(this.hap.Characteristic.OccupancyDetected)
|
|
63
|
+
.onGet(this.getOccupancy.bind(this));
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
if (this.stateful) {
|
|
67
|
+
this.restoreState().catch((error: unknown) => {
|
|
68
|
+
this.log.error(String(error));
|
|
69
|
+
});
|
|
70
|
+
}
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
getServices(): Service[] {
|
|
74
|
+
const services = [
|
|
75
|
+
this.informationService,
|
|
76
|
+
this.lightbulbService
|
|
77
|
+
];
|
|
78
|
+
|
|
79
|
+
if (this.occupancyService) {
|
|
80
|
+
services.push(this.occupancyService);
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
return services;
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
private async getOn(): Promise<boolean> {
|
|
87
|
+
await this.restoreState();
|
|
88
|
+
return this.brightness > 0;
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
private async setOn(value: CharacteristicValue): Promise<void> {
|
|
92
|
+
if (!(value as boolean)) {
|
|
93
|
+
this.log.info("Manually stopping timer.");
|
|
94
|
+
await this.updateBrightness(0);
|
|
95
|
+
}
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
private async getBrightness(): Promise<number> {
|
|
99
|
+
await this.restoreState();
|
|
100
|
+
return this.brightness;
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
private async setBrightness(value: CharacteristicValue): Promise<void> {
|
|
104
|
+
const brightness = value as number;
|
|
105
|
+
await this.updateBrightness(brightness);
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
private async getOccupancy(): Promise<boolean> {
|
|
109
|
+
await this.restoreState();
|
|
110
|
+
return this.brightness > 0;
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
private async updateBrightness(value: number): Promise<void> {
|
|
114
|
+
this.log.debug(`Brightness: ${this.brightness.toString()} -> ${value.toString()}`);
|
|
115
|
+
this.brightness = Math.max(0, Math.min(100, value));
|
|
116
|
+
|
|
117
|
+
// Persist state
|
|
118
|
+
if (this.stateful) {
|
|
119
|
+
if (this.brightness > 0) {
|
|
120
|
+
this.log.debug("Caching state");
|
|
121
|
+
await set(this.storageKey, this.brightness);
|
|
122
|
+
} else {
|
|
123
|
+
this.log.debug("Deleting state");
|
|
124
|
+
await del(this.storageKey);
|
|
125
|
+
}
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
// Update HomeKit
|
|
129
|
+
const isActive = this.brightness > 0;
|
|
130
|
+
this.lightbulbService.updateCharacteristic(this.hap.Characteristic.Brightness, this.brightness);
|
|
131
|
+
this.lightbulbService.updateCharacteristic(this.hap.Characteristic.On, isActive);
|
|
132
|
+
if (this.occupancyService !== undefined) {
|
|
133
|
+
this.occupancyService.updateCharacteristic(this.hap.Characteristic.OccupancyDetected, isActive);
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
// Update Timer
|
|
137
|
+
if (isActive && this.timer === undefined) {
|
|
138
|
+
this.log.info("Starting timer");
|
|
139
|
+
this.timer = setInterval(() => {
|
|
140
|
+
this.updateBrightness(this.brightness - 1).catch((error: unknown) => {
|
|
141
|
+
this.log.error(String(error));
|
|
142
|
+
});
|
|
143
|
+
}, this.interval);
|
|
144
|
+
} else if (this.brightness === 0) {
|
|
145
|
+
this.log.info("Timer completed.");
|
|
146
|
+
clearInterval(this.timer);
|
|
147
|
+
this.timer = undefined;
|
|
148
|
+
}
|
|
149
|
+
}
|
|
150
|
+
|
|
151
|
+
private async restoreState(): Promise<void> {
|
|
152
|
+
if (!this.stateful || this.stateRestored) {
|
|
153
|
+
return;
|
|
154
|
+
}
|
|
155
|
+
|
|
156
|
+
await this.lock.acquire("restoreState", async () => {
|
|
157
|
+
// Double-lock
|
|
158
|
+
if (this.stateRestored) {
|
|
159
|
+
return;
|
|
160
|
+
}
|
|
161
|
+
|
|
162
|
+
this.log.debug("Checking for stored state.");
|
|
163
|
+
await init({
|
|
164
|
+
expiredInterval: 1000 * 60 * 60 * 24 * 14, // Delete cached items after 14 days.
|
|
165
|
+
forgiveParseErrors: true,
|
|
166
|
+
dir: this.storageDir
|
|
167
|
+
});
|
|
168
|
+
|
|
169
|
+
const value = await get(this.storageKey) as number | undefined;
|
|
170
|
+
if (value !== undefined && value > 0) {
|
|
171
|
+
this.log.info(`Restoring state to: ${value.toString()}`);
|
|
172
|
+
await this.updateBrightness(value);
|
|
173
|
+
}
|
|
174
|
+
|
|
175
|
+
this.stateRestored = true;
|
|
176
|
+
});
|
|
177
|
+
}
|
|
178
|
+
}
|
|
179
|
+
|
|
180
|
+
export = (api: API) => {
|
|
181
|
+
api.registerAccessory("EggTimerBulb", EggTimerBulb);
|
|
182
|
+
};
|
package/tsconfig.json
ADDED
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
{
|
|
2
|
+
"compilerOptions": {
|
|
3
|
+
"target": "ES2018", // ~node10
|
|
4
|
+
"module": "commonjs",
|
|
5
|
+
"lib": [
|
|
6
|
+
"es2015",
|
|
7
|
+
"es2016",
|
|
8
|
+
"es2017",
|
|
9
|
+
"es2018"
|
|
10
|
+
],
|
|
11
|
+
"declaration": true,
|
|
12
|
+
"declarationMap": true,
|
|
13
|
+
"sourceMap": true,
|
|
14
|
+
"outDir": "./dist",
|
|
15
|
+
"rootDir": "./src",
|
|
16
|
+
"strict": true,
|
|
17
|
+
"esModuleInterop": true,
|
|
18
|
+
"noImplicitAny": false
|
|
19
|
+
},
|
|
20
|
+
"include": [
|
|
21
|
+
"src/"
|
|
22
|
+
]
|
|
23
|
+
}
|