-
-
Notifications
You must be signed in to change notification settings - Fork 0
/
grid-eye.service.ts
196 lines (173 loc) · 5.52 KB
/
grid-eye.service.ts
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
import {
Injectable,
Logger,
OnApplicationBootstrap,
OnApplicationShutdown,
} from '@nestjs/common';
import i2cBus, { PromisifiedBus } from 'i2c-bus';
import { Entity } from '../../entities/entity.dto';
import { EntitiesService } from '../../entities/entities.service';
import { Sensor } from '../../entities/sensor';
import * as math from 'mathjs';
import { Interval } from '@nestjs/schedule';
import { ThermopileOccupancyService } from '../thermopile/thermopile-occupancy.service';
import { GridEyeConfig } from './grid-eye.config';
import { ConfigService } from '../../config/config.service';
import { SensorConfig } from '../home-assistant/sensor-config';
import { EntityCustomization } from '../../entities/entity-customization.interface';
import { Camera } from '../../entities/camera';
const TEMPERATURE_REGISTER_START = 0x80;
const FRAMERATE_REGISTER = 0x02;
@Injectable()
export class GridEyeService extends ThermopileOccupancyService
implements OnApplicationBootstrap, OnApplicationShutdown {
private readonly config: GridEyeConfig;
private readonly logger: Logger;
private i2cBus: PromisifiedBus;
private sensor: Entity;
private camera: Camera;
constructor(
private readonly entitiesService: EntitiesService,
private readonly configService: ConfigService
) {
super();
this.config = this.configService.get('gridEye');
this.logger = new Logger(GridEyeService.name);
}
/**
* Lifecycle hook, called once the application has started.
*/
async onApplicationBootstrap(): Promise<void> {
this.logger.log(`Opening i2c bus ${this.config.busNumber}`);
this.i2cBus = await i2cBus.openPromisified(this.config.busNumber);
this.setRegister(FRAMERATE_REGISTER, 1); // set framerate to 1 FPS -> less noise
this.sensor = this.createSensor();
if (this.isHeatmapAvailable()) {
this.camera = this.createHeatmapCamera();
} else {
this.logger.warn(
'Heatmap is unavailable due to the canvas dependency not being installed'
);
}
}
/**
* Lifecycle hook, called once the application is shutting down.
*/
async onApplicationShutdown(): Promise<void> {
this.logger.log(`Closing i2c bus ${this.config.busNumber}`);
return this.i2cBus.close();
}
/**
* Updates the state of the sensor that this integration manages.
*/
@Interval(1000)
async updateState(): Promise<void> {
const temperatures = await this.getPixelTemperatures();
const coordinates = await this.getCoordinates(
temperatures,
this.config.deltaThreshold
);
this.sensor.state = coordinates.length;
this.sensor.attributes.coordinates = coordinates;
if (this.camera != undefined) {
this.camera.state = await this.generateHeatmap(
temperatures,
this.config.heatmap
);
}
}
/**
* Gets the temperatures of all sensor pixels.
*
* @returns 8x8 matrix of temperatures
*/
async getPixelTemperatures(): Promise<number[][]> {
const temperatures = [];
for (let i = 0; i < 64; i++) {
temperatures.push(await this.getPixelTemperature(i));
}
return math.reshape(temperatures, [8, 8]) as number[][];
}
/**
* Gets the temperature of a single sensor pixel.
*
* @param pixelAddr - Index of the pixel, between 0 and 63
* @returns Temperature sensed by the pixel
*/
async getPixelTemperature(pixelAddr: number): Promise<number> {
const pixelLowRegister = TEMPERATURE_REGISTER_START + 2 * pixelAddr;
let temperature = await this.getRegister(pixelLowRegister, 2);
if (temperature & (1 << 11)) {
temperature &= ~(1 << 11);
temperature = temperature * -1;
}
return temperature * 0.25;
}
/**
* Gets a value from a given register.
*
* @param register - Sensor register address
* @param length - Length of the value to retrieve
* @returns Register value
*/
async getRegister(register: number, length: number): Promise<number> {
const commandBuffer = Buffer.alloc(1);
commandBuffer.writeUInt8(register, 0);
const resultBuffer = Buffer.alloc(length);
await this.i2cBus.i2cWrite(
this.config.address,
commandBuffer.length,
commandBuffer
);
await this.i2cBus.i2cRead(this.config.address, length, resultBuffer);
const lsb = resultBuffer.readUInt8(0);
const msb = resultBuffer.readUInt8(1);
return (msb << 8) | lsb;
}
/**
* Sets a value in a given register.
*
* @param register - Sensor register address
* @param value - Value to write
*/
async setRegister(register: number, value: number): Promise<void> {
const commandBuffer = Buffer.alloc(2);
commandBuffer.writeUInt8(register, 0);
commandBuffer.writeUInt8(value, 1);
await this.i2cBus.i2cWrite(
this.config.address,
commandBuffer.length,
commandBuffer
);
}
/**
* Creates and registers a new occupancy count sensor.
*
* @returns Registered sensor
*/
private createSensor(): Sensor {
const customizations: Array<EntityCustomization<any>> = [
{
for: SensorConfig,
overrides: {
icon: 'mdi:account',
unitOfMeasurement: 'person',
},
},
];
return this.entitiesService.add(
new Sensor('grideye_occupancy_count', 'GridEYE Occupancy Count'),
customizations
) as Sensor;
}
/**
* Creates and registers a new heatmap camera entity.
*
* @returns Registered camera
*/
protected createHeatmapCamera(): Camera {
return this.entitiesService.add(
new Camera('grideye_heatmap', 'GridEYE Heatmap')
) as Camera;
}
}