Skip to content
Closed
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
5 changes: 5 additions & 0 deletions Sources/HAP/Endpoints/Protocol.swift
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,7 @@ enum Protocol {
case int(Int)
case double(Double)
case string(String)
case bool(Bool)

enum DecodeError: Error {
case unsupportedValueType
Expand All @@ -57,6 +58,8 @@ enum Protocol {
self = .double(double)
} else if let string = try? container.decode(String.self) {
self = .string(string)
} else if let bool = try? container.decode(Bool.self) {
self = .bool(bool)
} else {
throw DecodeError.unsupportedValueType
}
Expand All @@ -71,6 +74,8 @@ enum Protocol {
try container.encode(double)
case let .string(string):
try container.encode(string)
case let .bool(bool):
try container.encode(bool)
}
}
}
Expand Down
11 changes: 7 additions & 4 deletions Sources/HAP/Endpoints/characteristics().swift
Original file line number Diff line number Diff line change
Expand Up @@ -104,10 +104,11 @@ func characteristics(device: Device) -> Application {

case "PUT":
var body = Data()
guard
(try? request.readAllData(into: &body)) != nil,
let decoded = try? JSONDecoder().decode(Protocol.CharacteristicContainer.self, from: body) else
{
let byteCount = try? request.readAllData(into: &body)
logger.debug("PUT data: \(String(bytes: body, encoding: .utf8))")
guard byteCount != nil,
let decoded = try? JSONDecoder().decode(Protocol.CharacteristicContainer.self, from: body)
else {
logger.warning("Could not decode JSON")
return .badRequest
}
Expand Down Expand Up @@ -148,6 +149,8 @@ func characteristics(device: Device) -> Application {
try characteristic.setValue(int, fromConnection: connection)
case let .double(double):
try characteristic.setValue(double, fromConnection: connection)
case let .bool(bool):
try characteristic.setValue(bool, fromConnection: connection)
}
status.status = .success
} catch {
Expand Down
2 changes: 1 addition & 1 deletion Sources/HAP/Server/Configuration.swift
Original file line number Diff line number Diff line change
Expand Up @@ -42,7 +42,7 @@ extension Device {
// Accessories must increment the config number after a firmware update.
// This must have a range of 1-4294967295 and wrap to 1 when it overflows.
// This value must persist across reboots, power cycles, etc.
internal var number: UInt32 = 0
internal var number: UInt32 = 1

// HAP Specification 2.6.1: Instance IDs
//
Expand Down
36 changes: 33 additions & 3 deletions Sources/HAP/Server/Device.swift
Original file line number Diff line number Diff line change
Expand Up @@ -150,7 +150,7 @@ public class Device {
// The first accessory must be aid 1
accessories[0].aid = 1

addAccessories(accessories)
addToAccessoryList(accessories)
}

private func persistConfig() {
Expand Down Expand Up @@ -185,7 +185,7 @@ public class Device {
/// It is an error to try and add accessories with duplicate serial numbers.
/// It is an error to try and add accessories to a non-bridge device.
/// It is an error to try and increase the number of accessories above 99.
public func addAccessories(_ newAccessories: [Accessory]) {
private func addToAccessoryList(_ newAccessories: [Accessory]) {
let totalNumberOfAccessories = accessories.count + newAccessories.count
precondition(
(isBridge && totalNumberOfAccessories <= 100) ||
Expand Down Expand Up @@ -228,6 +228,18 @@ public class Device {
configuration.aidForAccessorySerialNumber[serialNumber] = accessory.aid
}
}
}

/// Add an array of accessories to this bridge device, and notify changes
///
/// It is an error to try and add accessories with duplicate serial numbers.
/// It is an error to try and add accessories to a non-bridge device.
/// It is an error to try and increase the number of accessories above 99.
public func addAccessories(_ newAccessories: [Accessory]) {

addToAccessoryList(newAccessories)

delegate?.didChangeAccessoryList()

// Write configuration data to persist updated aid's and notify listeners
updatedConfiguration()
Expand Down Expand Up @@ -266,6 +278,8 @@ public class Device {
let serialNumber = accessory.serialNumber
configuration.aidForAccessorySerialNumber.removeValue(forKey: serialNumber)
}
delegate?.didChangeAccessoryList()

// write configuration data to persist updated aid's
updatedConfiguration()
}
Expand Down Expand Up @@ -317,6 +331,22 @@ public class Device {
return pairingState == .paired
}

// Remove all the pairings made with this Device
// Can be used in the event of a stale configuration file
public func unpairFromAllControllers() {
logger.debug("Before unpair")
logger.debug(self.configuration)
logger.debug(self.config)
server?.stop()
let allPairingIdentifiers = configuration.pairings.keys
for identifier in allPairingIdentifiers {
remove(pairingWithIdentifier: identifier)
}
logger.debug("After unpair")
logger.debug(self.configuration)
logger.debug(self.config)
}

// Add the pairing to the internal DB and notify the change
// to update the Bonjour broadcast
func add(pairing: Pairing) {
Expand All @@ -339,7 +369,7 @@ public class Device {
configuration.pairings = [:]
}
persistConfig()
if pairingState == .paired {
if pairingState == .paired && configuration.pairings.isEmpty {
// swiftlint:disable:next force_try
try! changePairingState(.notPaired)
}
Expand Down
6 changes: 6 additions & 0 deletions Sources/HAP/Server/DeviceDelegate.swift
Original file line number Diff line number Diff line change
Expand Up @@ -51,6 +51,10 @@ public protocol DeviceDelegate: class {
///
func didChangePairingState(from: PairingState, to: PairingState)

/// Tells the delegate that one or more Accessories were added or removed.
///
func didChangeAccessoryList()

/// Tells the delegate that the value of a characteristic has changed.
///
/// - Parameters:
Expand Down Expand Up @@ -83,6 +87,8 @@ public extension DeviceDelegate {

func didChangePairingState(from: PairingState, to: PairingState) { }

func didChangeAccessoryList() { }

func characteristic<T>(
_ characteristic: GenericCharacteristic<T>,
ofService: Service,
Expand Down
47 changes: 41 additions & 6 deletions Sources/HAP/Server/Server.swift
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,25 @@ public class Server: NSObject, NetServiceDelegate {
let listenSocket: Socket
let socketLockQueue = DispatchQueue(label: "hap.socketLockQueue")

var continueRunning = true
let continueRunningLock = DispatchSemaphore(value: 1)

// swiftlint:disable:next identifier_name
var _continueRunning = false
var continueRunning: Bool {
get {
continueRunningLock.wait()
defer {
continueRunningLock.signal()
}
return _continueRunning
}
set {
continueRunningLock.wait()
_continueRunning = newValue
continueRunningLock.signal()
}

}
var connectedSockets = [Int32: Socket]()

public init(device: Device, port: Int = 0) throws {
Expand Down Expand Up @@ -51,6 +69,9 @@ public class Server: NSObject, NetServiceDelegate {
}

public func start() {
if #available(OSX 10.12, *) {
dispatchPrecondition(condition: .onQueue(.main))
}
// TODO: make sure can only be started if not started

continueRunning = true
Expand All @@ -62,27 +83,41 @@ public class Server: NSObject, NetServiceDelegate {
do {
repeat {
let newSocket = try self.listenSocket.acceptClientConnection()
logger.info("Accepted connection from \(newSocket.remoteHostname)")
DispatchQueue.main.async {
logger.info("Accepted connection from \(newSocket.remoteHostname)")
}
self.addNewConnection(socket: newSocket)
} while self.continueRunning
} catch {
logger.error("Could not accept connections for listening socket", error: error)
}
self.stop()
self.tearDownConnections()
self.listenSocket.close()
}
}

public func stop() {
func tearDownConnections() {
service.stop()
continueRunning = false
listenSocket.close()

socketLockQueue.sync { [unowned self] in
for socket in self.connectedSockets {
socket.value.close()
Socket.forceClose(socketfd: socket.key)
}
self.connectedSockets = [:]
}

}

public func stop() {
if #available(OSX 10.12, *) {
dispatchPrecondition(condition: .onQueue(.main))
}
continueRunning = false

//listenSocket.close()
Socket.forceClose(socketfd: listenSocket.socketfd)
//tearDownConnections()
}

func addNewConnection(socket: Socket) {
Expand Down
34 changes: 34 additions & 0 deletions Sources/HAP/Utils/Socket+forceClose.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
//
// Socket+forceClose.swift
// HAP
//
// Created by Guy Brooker on 10/10/2018.
//

import Foundation
import Socket

#if os(macOS) || os(iOS) || os(tvOS)
import Darwin
#elseif os(Linux)
import Glibc
#endif

extension Socket {

// Avoid race conditions in BlueSocket when closing a socket from another
// thread by closing the sockets file descriptor using system calls.
//
// Assumes socket is being listened to and is connected
//
// swiftlint:disable:next identifier_name
static func forceClose(socketfd fd: Int32) {
#if os(Linux)
_ = Glibc.shutdown(fd, Int32(SHUT_RDWR))
_ = Glibc.close(fd)
#else
_ = Darwin.shutdown(fd, Int32(SHUT_RDWR))
_ = Darwin.close(fd)
#endif
}
}