Merge branch 'buzzert/offline_fix' into 'master'
Fixes issue where connectivity issues can lead to persistent "Offline" status in UI See merge request zanneth/XIONControlPanel!2
This commit is contained in:
@@ -206,11 +206,6 @@ class MainViewController: UIViewController, SwitchesViewControllerDelegate, Serv
|
||||
{
|
||||
_switchesController.devices = Array(multiplex.devices)
|
||||
_updateVisualization(false)
|
||||
|
||||
// Update connection status too, if applicable
|
||||
if _serverMultiplex.numServers == _serverMultiplex.numberOfConnectedServers() {
|
||||
_updateConnectivityStatus(.connected)
|
||||
}
|
||||
}
|
||||
|
||||
func serverMultiplex(_ multiplex: ServerMultiplex, devicesStateChanged devices: [AnyDevice])
|
||||
@@ -218,6 +213,13 @@ class MainViewController: UIViewController, SwitchesViewControllerDelegate, Serv
|
||||
_switchesController.devicesStateChanged(devices)
|
||||
}
|
||||
|
||||
func serverMultiplex(_ multiplex: ServerMultiplex, didReceiveAcknowledgementFromServer server: Server)
|
||||
{
|
||||
// Update connection status too, if applicable
|
||||
let groupConnectionStatus = multiplex.groupConnectionStatus()
|
||||
_updateConnectivityStatus(groupConnectionStatus)
|
||||
}
|
||||
|
||||
func serverMultiplex(_ multiplex: ServerMultiplex, didEncounterError error: Error)
|
||||
{
|
||||
_updateConnectivityStatus(.error)
|
||||
|
||||
@@ -16,7 +16,7 @@ public enum HubitatServerError : Error
|
||||
|
||||
public class HubitatServer : Server
|
||||
{
|
||||
var connected: Bool = false
|
||||
var connectionStatus: ConnectionStatus = .disconnected
|
||||
|
||||
public fileprivate(set) var devices: [HubitatDevice] = []
|
||||
|
||||
@@ -33,13 +33,13 @@ public class HubitatServer : Server
|
||||
func connect(_ completion: @escaping (Error?) -> Void)
|
||||
{
|
||||
// RESTful, assume we're connected unless we get an error
|
||||
self.connected = true
|
||||
connectionStatus = .connected
|
||||
completion(nil)
|
||||
}
|
||||
|
||||
func disconnect(_ completion: (Error?) -> Void)
|
||||
{
|
||||
self.connected = false
|
||||
connectionStatus = .disconnected
|
||||
}
|
||||
|
||||
func fetchDevices(_ fetchCompletion: @escaping (Result<[AnyDevice], Error>) -> Void)
|
||||
@@ -52,10 +52,10 @@ public class HubitatServer : Server
|
||||
.decode(type: [HubitatDevice].self, decoder: JSONDecoder())
|
||||
.sink(receiveCompletion: { completion in
|
||||
if case let Subscribers.Completion.failure(error) = completion {
|
||||
self.connected = false
|
||||
self.connectionStatus = .error
|
||||
fetchCompletion(.failure(error))
|
||||
} else {
|
||||
self.connected = true
|
||||
self.connectionStatus = .connected
|
||||
}
|
||||
}) { (devices: [HubitatDevice]) in
|
||||
self.devices = devices
|
||||
|
||||
@@ -12,6 +12,7 @@ protocol ServerMultiplexDelegate
|
||||
{
|
||||
func serverMultiplex(_ multiplex: ServerMultiplex, didAddDevices devices: [AnyDevice])
|
||||
func serverMultiplex(_ multiplex: ServerMultiplex, devicesStateChanged devices: [AnyDevice])
|
||||
func serverMultiplex(_ multiplex: ServerMultiplex, didReceiveAcknowledgementFromServer server: Server)
|
||||
func serverMultiplex(_ multiplex: ServerMultiplex, didEncounterError error: Error)
|
||||
}
|
||||
|
||||
@@ -33,9 +34,26 @@ class ServerMultiplex
|
||||
servers.append(server)
|
||||
}
|
||||
|
||||
public func numberOfConnectedServers() -> Int
|
||||
/// Returns a status that best represents the status of the whole group, as if it were one device
|
||||
public func groupConnectionStatus() -> ConnectionStatus
|
||||
{
|
||||
return servers.filter { $0.connected == true }.count
|
||||
let relevancyRank: (ConnectionStatus) -> Int = { status in
|
||||
switch status {
|
||||
case .disconnected: return 0
|
||||
case .connected: return 1
|
||||
case .connecting: return 2
|
||||
case .error: return 3
|
||||
}
|
||||
}
|
||||
|
||||
var groupStatus = ConnectionStatus.connected
|
||||
servers.forEach { server in
|
||||
if relevancyRank(server.connectionStatus) > relevancyRank(groupStatus) {
|
||||
groupStatus = server.connectionStatus
|
||||
}
|
||||
}
|
||||
|
||||
return groupStatus
|
||||
}
|
||||
|
||||
public func toggleDeviceState(_ device: AnyDevice, state: DeviceState, completion: @escaping (Error?) -> Void)
|
||||
@@ -56,7 +74,7 @@ class ServerMultiplex
|
||||
{
|
||||
self.servers.forEach { (server: Server) in
|
||||
server.fetchDevices { (result: Result<[AnyDevice], Error>) in
|
||||
self.handleServerFetchResult(result)
|
||||
self.handleServerFetchResult(forServer: server, result: result)
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -64,18 +82,22 @@ class ServerMultiplex
|
||||
|
||||
extension ServerMultiplex
|
||||
{
|
||||
private func handleServerFetchResult(_ result: Result<[AnyDevice], Error>)
|
||||
private func handleServerFetchResult(forServer server: Server, result: Result<[AnyDevice], Error>)
|
||||
{
|
||||
switch result {
|
||||
case .success(let devices):
|
||||
handleDevicesChanged(devices)
|
||||
handleDevicesChanged(forServer: server, devicesChanged: devices)
|
||||
case .failure(let error):
|
||||
handleError(error)
|
||||
handleError(forServer: server, error: error)
|
||||
}
|
||||
}
|
||||
|
||||
private func handleDevicesChanged(_ devicesChanged: [AnyDevice])
|
||||
private func handleDevicesChanged(forServer server: Server, devicesChanged: [AnyDevice])
|
||||
{
|
||||
// First send server acknowledgement
|
||||
self.delegate?.serverMultiplex(self, didReceiveAcknowledgementFromServer: server)
|
||||
|
||||
// Then, optionally notify about new devices or device state changes
|
||||
let newDevicesSet = Set<AnyDevice>(devicesChanged)
|
||||
let additions = newDevicesSet.subtracting(self.devices)
|
||||
let changed = newDevicesSet.filter { (device: AnyDevice) in
|
||||
@@ -101,7 +123,7 @@ extension ServerMultiplex
|
||||
}
|
||||
}
|
||||
|
||||
private func handleError(_ error: Error)
|
||||
private func handleError(forServer server: Server, error: Error)
|
||||
{
|
||||
DispatchQueue.main.async {
|
||||
self.delegate?.serverMultiplex(self, didEncounterError: error)
|
||||
|
||||
@@ -8,9 +8,17 @@
|
||||
|
||||
import Foundation
|
||||
|
||||
enum ConnectionStatus
|
||||
{
|
||||
case disconnected
|
||||
case connecting
|
||||
case connected
|
||||
case error
|
||||
}
|
||||
|
||||
protocol Server
|
||||
{
|
||||
var connected: Bool { get }
|
||||
var connectionStatus: ConnectionStatus { get }
|
||||
|
||||
/// Designated initializer. Takes an API endpoint URL
|
||||
init(_ url: URL)
|
||||
|
||||
@@ -8,14 +8,6 @@
|
||||
|
||||
import Foundation
|
||||
|
||||
enum ConnectionStatus
|
||||
{
|
||||
case disconnected
|
||||
case connecting
|
||||
case connected
|
||||
case error
|
||||
}
|
||||
|
||||
enum ConnectionError : Error
|
||||
{
|
||||
case unknown
|
||||
@@ -25,11 +17,9 @@ enum ConnectionError : Error
|
||||
class WemoServer : Server
|
||||
{
|
||||
private var devices: [WemoDevice] = []
|
||||
|
||||
public var connected: Bool { get { return self.connectionStatus == .connected } }
|
||||
public var connectionStatus: ConnectionStatus = .disconnected
|
||||
|
||||
fileprivate(set) var baseURL: URL
|
||||
fileprivate(set) var connectionStatus: ConnectionStatus = .disconnected
|
||||
|
||||
fileprivate var _urlSession: URLSession
|
||||
fileprivate var _errorStream: StandardErrorOutputStream = StandardErrorOutputStream()
|
||||
@@ -48,6 +38,8 @@ class WemoServer : Server
|
||||
func connect(_ completion: @escaping (Error?) -> Void)
|
||||
{
|
||||
if (self.connectionStatus == .disconnected) {
|
||||
self.connectionStatus = .connecting
|
||||
|
||||
let op = ConnectOperation(baseURL: self.baseURL, session: _urlSession)
|
||||
weak var weakOp = op
|
||||
op.completionBlock = {
|
||||
@@ -103,7 +95,7 @@ class WemoServer : Server
|
||||
|
||||
func toggleDevice(_ device: AnyDevice, state: DeviceState, completion: @escaping (Error?) -> Void)
|
||||
{
|
||||
if self.connected, let device = findDevice(device) {
|
||||
if connectionStatus == .connected, let device = findDevice(device) {
|
||||
let op = ToggleDeviceOperation(baseURL: self.baseURL, session: _urlSession, device: device, state: state)
|
||||
weak var weakOp = op
|
||||
op.completionBlock = {
|
||||
|
||||
Reference in New Issue
Block a user