Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,9 @@ jobs:
- name: Install dependencies
run: npm ci

- name: Typecheck
run: npm run typecheck

- name: Lint
if: matrix['node-version'] == env.PRIMARY_NODE_VERSION
run: npm run lint
Expand Down
85 changes: 48 additions & 37 deletions api/app.ts
Original file line number Diff line number Diff line change
Expand Up @@ -156,6 +156,14 @@ export function createApp(options: CreateAppOptions = {}): AppInstance {

const socketManager = new SocketManager()

function requireSocketServer(): SocketIOServer {
const io = socketManager.io
if (!io) {
throw new Error('Socket.IO is not attached')
}
return io
}

const runtime = new AppRuntime({
getSocketServer: () => socketManager.io,
gatewayFactory: new GatewayFactory({
Expand Down Expand Up @@ -231,7 +239,7 @@ export function createApp(options: CreateAppOptions = {}): AppInstance {
* Start http/https server and all the manager
*/
async function startServer(port: number | string, host?: string) {
let server: HttpServer
let server: HttpServer | undefined

installProcessHandlers()

Expand Down Expand Up @@ -281,9 +289,10 @@ export function createApp(options: CreateAppOptions = {}): AppInstance {
server = createHttpServer(app)
}

attachSocket(server)
server.listen(port as number, host, function () {
const addr = server.address()
const listeningServer = server
attachSocket(listeningServer)
listeningServer.listen(port as number, host, function () {
const addr = listeningServer.address()
const bind =
typeof addr === 'string'
? 'pipe ' + addr
Expand All @@ -295,28 +304,33 @@ export function createApp(options: CreateAppOptions = {}): AppInstance {
)
})

server.on('error', function (error: NodeJS.ErrnoException) {
if (error.syscall !== 'listen') {
throw error
}

const bind =
typeof port === 'string' ? 'Pipe ' + port : 'Port ' + port

// handle specific listen errors with friendly messages
switch (error.code) {
case 'EACCES':
logger.error(bind + ' requires elevated privileges')
process.exit(1)
break
case 'EADDRINUSE':
logger.error(bind + ' is already in use')
process.exit(1)
break
default:
listeningServer.on(
'error',
function (error: NodeJS.ErrnoException) {
if (error.syscall !== 'listen') {
throw error
}
})
}

const bind =
typeof port === 'string'
? 'Pipe ' + port
: 'Port ' + port

// handle specific listen errors with friendly messages
switch (error.code) {
case 'EACCES':
logger.error(bind + ' requires elevated privileges')
process.exit(1)
break
case 'EADDRINUSE':
logger.error(bind + ' is already in use')
process.exit(1)
break
default:
throw error
}
},
)

const users = jsonStore.get(store.users)

Expand All @@ -334,7 +348,7 @@ export function createApp(options: CreateAppOptions = {}): AppInstance {
await debugManager.init() // Clean up any old debug temp files
await runtime.startGateway(settings)

return server
return listeningServer
} catch (error) {
try {
await close()
Expand All @@ -349,16 +363,16 @@ export function createApp(options: CreateAppOptions = {}): AppInstance {
}

async function loadCertKey(): Promise<{
cert: string
key: string
cert: string | undefined
key: string | undefined
}> {
const certFile =
process.env.SSL_CERTIFICATE || utils.joinPath(storeDir, 'cert.pem')
const keyFile =
process.env.SSL_KEY || utils.joinPath(storeDir, 'key.pem')

let key: string
let cert: string
let key: string | undefined
let cert: string | undefined

try {
cert = await readFile(certFile, 'utf8')
Expand Down Expand Up @@ -404,11 +418,11 @@ export function createApp(options: CreateAppOptions = {}): AppInstance {
loggers.logStream.off('data', logStreamInterceptor)
}

const io = requireSocketServer()

// intercept logs and redirect them to socket
const interceptor: (chunk: Buffer | string) => void = (chunk) => {
socketManager.io
.to('debug')
.emit(socketEvents.debug, chunk.toString())
io.to('debug').emit(socketEvents.debug, chunk.toString())
}
logStreamInterceptor = interceptor
loggers.logStream.on('data', interceptor)
Expand Down Expand Up @@ -635,10 +649,7 @@ export function createApp(options: CreateAppOptions = {}): AppInstance {
app,
attachSocket,
get io() {
if (!socketManager.io) {
throw new Error('Socket.IO is not attached')
}
return socketManager.io
return requireSocketServer()
},
startServer,
loadSnippets: () => runtime.loadSnippets(),
Expand Down
12 changes: 7 additions & 5 deletions api/hass/DiscoveryGenerator.ts
Original file line number Diff line number Diff line change
Expand Up @@ -928,14 +928,16 @@ export class DiscoveryGenerator {
let isSensor = true
if (commandClass === CommandClasses['Multilevel Sensor']) {
// Skip auxiliary values because zwave-js omits their sensor metadata
if (!valueId.ccSpecific) return
const sensorType = valueId.ccSpecific.sensorType
const ccSpecific = valueId.ccSpecific
if (!ccSpecific) return
const sensorType = ccSpecific.sensorType
if (typeof sensorType !== 'number') return
sensor = Constants.sensorType(sensorType)
} else if (commandClass === CommandClasses.Meter) {
if (!valueId.ccSpecific) return
const meterType = valueId.ccSpecific.meterType
const scale = valueId.ccSpecific.scale
const ccSpecific = valueId.ccSpecific
if (!ccSpecific) return
const meterType = ccSpecific.meterType
const scale = ccSpecific.scale
if (
typeof meterType !== 'number' ||
typeof scale !== 'number'
Expand Down
2 changes: 1 addition & 1 deletion api/hass/ports.ts
Original file line number Diff line number Diff line change
Expand Up @@ -51,7 +51,7 @@ export interface HassValueConfiguration {
export interface HassValueTopic {
topic: string
valueConf?: HassValueConfiguration
targetTopic?: string
targetTopic?: string | null
}

export interface HassTopicPort {
Expand Down
8 changes: 7 additions & 1 deletion api/lib/DebugManager.ts
Original file line number Diff line number Diff line change
Expand Up @@ -261,7 +261,13 @@ class DebugManager {
session.driverDebugTransport,
)
if (session.zwaveClient.driverReady) {
session.zwaveClient.driver.updateLogConfig({
const driver = session.zwaveClient.driver
if (!driver) {
throw new TypeError(
'Driver is unavailable while restoring the debug log level',
)
}
driver.updateLogConfig({
level: session.originalLogLevel,
})
}
Expand Down
Loading
Loading