diff --git a/PresentationLayer/Constants/Strings/DisplayedLinks.swift b/PresentationLayer/Constants/Strings/DisplayedLinks.swift index 224a6745a..ee8de8f64 100644 --- a/PresentationLayer/Constants/Strings/DisplayedLinks.swift +++ b/PresentationLayer/Constants/Strings/DisplayedLinks.swift @@ -24,6 +24,7 @@ enum DisplayedLinks { case m5Troubleshooting case heliumTroubleshooting case d1Troubleshooting + case pulseTroubleshooting case heliumRegionFrequencies case shopLink case shareDevice @@ -74,6 +75,8 @@ enum DisplayedLinks { return "https://docs.weatherxm.com/wxm-devices/helium/troubleshooting" case .d1Troubleshooting: return "https://docs.weatherxm.com/wxm-devices/d1/troubleshooting" + case .pulseTroubleshooting: + return "https://docs.weatherxm.com/wxm-devices/pulse/troubleshooting" case .heliumRegionFrequencies: return "https://docs.helium.com/iot/lorawan-region-plans" case .shopLink: diff --git a/PresentationLayer/Extensions/DomainExtensions/DeviceDetails+.swift b/PresentationLayer/Extensions/DomainExtensions/DeviceDetails+.swift index 4d4c85062..e54a08af0 100644 --- a/PresentationLayer/Extensions/DomainExtensions/DeviceDetails+.swift +++ b/PresentationLayer/Extensions/DomainExtensions/DeviceDetails+.swift @@ -52,7 +52,7 @@ extension DeviceDetails { case .d1: return DisplayedLinks.d1Troubleshooting.linkURL case .pulse: - return nil + return DisplayedLinks.pulseTroubleshooting.linkURL } } diff --git a/PresentationLayer/UIComponents/Screens/DeviceInfo/DeviceInfoFactory.swift b/PresentationLayer/UIComponents/Screens/DeviceInfo/DeviceInfoFactory.swift index 41fd48afe..209f2f76f 100644 --- a/PresentationLayer/UIComponents/Screens/DeviceInfo/DeviceInfoFactory.swift +++ b/PresentationLayer/UIComponents/Screens/DeviceInfo/DeviceInfoFactory.swift @@ -194,10 +194,15 @@ extension DeviceInfoViewModel.Field { extension DeviceInfoViewModel.InfoField { static func getShareText(for device: DeviceDetails, deviceInfo: NetworkDevicesInfoResponse?, mainVM: MainScreenViewModel, followState: UserDeviceFollowState?) -> String { var fields: [Self] = [] - if device.isHelium { - fields = heliumFields - } else { - fields = wifiFields + switch device.bundle?.connectivity { + case .wifi: + fields = wifiFields + case .helium: + fields = heliumFields + case .cellular: + fields = pulseFields + case nil: + break } let textComps: [String] = fields.compactMap { field in @@ -286,6 +291,26 @@ extension DeviceInfoViewModel.InfoField { return LocalizableString.DeviceInfo.lastStationActivity.localized case .stationRssi: return LocalizableString.DeviceInfo.stationRssi.localized + case .gsmSignal: + return LocalizableString.DeviceInfo.gsmSignal.localized + case .gwFrequency: + return LocalizableString.DeviceInfo.gwFrequency.localized + case .externalSim: + return LocalizableString.DeviceInfo.externalSim.localized + case .iccid: + return LocalizableString.DeviceInfo.iccid.localized + case .mobileCountryCode: + return LocalizableString.DeviceInfo.mobileCountryCode.localized + case .gwBatteryState: + return LocalizableString.DeviceInfo.gwBatteryState.localized + case .signalGwStation: + return LocalizableString.DeviceInfo.signalGwStation.localized + case .nextServerCommunication: + return LocalizableString.DeviceInfo.nextServerCommunication.localized + case .stationId: + return LocalizableString.DeviceInfo.stationId.localized + case .signalStationGw: + return LocalizableString.DeviceInfo.signalStationGw.localized } } @@ -349,10 +374,43 @@ extension DeviceInfoViewModel.InfoField { case .lastStationActivity: return deviceInfo?.weatherStation?.lastActivity?.getFormattedDate(format: .monthLiteralYearDayTime, timezone: device.timezone?.toTimezone ?? .current).capitalizedSentence - case .stationRssi: + case .stationRssi, .signalStationGw: return Self.getRssiText(rssi: deviceInfo?.weatherStation?.stationRssi?.formatted(), lastActivityDate: deviceInfo?.weatherStation?.stationRssiLastActivity, timezone: device.timezone) + case .gsmSignal: + return Self.getRssiText(rssi: deviceInfo?.gateway?.networkRssi?.formatted(), + lastActivityDate: deviceInfo?.gateway?.networkRssiLastActivity, + timezone: device.timezone) + case .gwFrequency: + guard let frequency = deviceInfo?.gateway?.frequency else { + return nil + } + return "\(frequency) \(LocalizableString.Units.mhz.localized)" + case .externalSim: + guard deviceInfo?.gateway?.sim?.iccid != nil else { + return nil + } + + return LocalizableString.DeviceInfo.isInUse.localized + case .iccid: + return deviceInfo?.gateway?.sim?.iccid + case .mobileCountryCode: + guard let mcc = deviceInfo?.gateway?.sim?.mcc, let mnc = deviceInfo?.gateway?.sim?.mnc else { + return nil + } + return LocalizableString.DeviceInfo.mobileCountryNetworkCode(mcc, mnc).localized + case .gwBatteryState: + return deviceInfo?.gateway?.batState?.description + case .signalGwStation: + return Self.getRssiText(rssi: deviceInfo?.gateway?.gatewayRssi?.formatted(), + lastActivityDate: deviceInfo?.gateway?.gatewayRssiLastActivity, + timezone: device.timezone) + case .nextServerCommunication: + return deviceInfo?.gateway?.nextCommunication?.getFormattedDate(format: .monthLiteralYearDayTime, + timezone: device.timezone?.toTimezone ?? .current).capitalizedSentence + case .stationId: + return deviceInfo?.weatherStation?.stationId } } @@ -383,7 +441,7 @@ extension DeviceInfoViewModel.InfoField { case .wifiSignal: return nil case .batteryState: - return warningForBatteryState(deviceInfo?.weatherStation?.batState, deviceId: device.id ?? "") + return warningForBatteryState(deviceInfo?.weatherStation?.batState, deviceId: device.id ?? "", isGateway: false) case .claimedAt: return nil case .lastGatewayActivity: @@ -393,11 +451,31 @@ extension DeviceInfoViewModel.InfoField { case .lastStationActivity: return nil case .stationRssi: - return warningForStationRssi(for: deviceInfo?.weatherStation?.stationRssi) + return warningForStationRssi(for: deviceInfo?.weatherStation?.stationRssi, device: device) + case .gsmSignal: + return nil + case .gwFrequency: + return nil + case .externalSim: + return nil + case .iccid: + return nil + case .mobileCountryCode: + return nil + case .gwBatteryState: + return warningForBatteryState(deviceInfo?.gateway?.batState, deviceId: device.id ?? "", isGateway: true) + case .signalGwStation: + return nil + case .nextServerCommunication: + return nil + case .stationId: + return nil + case .signalStationGw: + return warningForStationRssi(for: deviceInfo?.weatherStation?.stationRssi, device: device) } } - func warningForBatteryState(_ state: BatteryState?, deviceId: String) -> (CardWarningConfiguration, VoidCallback)? { + func warningForBatteryState(_ state: BatteryState?, deviceId: String, isGateway: Bool) -> (CardWarningConfiguration, VoidCallback)? { guard let state else { return nil } @@ -409,8 +487,9 @@ extension DeviceInfoViewModel.InfoField { .action: .viewAction, .itemId: .custom(deviceId)]) } + let message = isGateway ? LocalizableString.DeviceInfo.gatewayLowBatteryWarningMarkdown : LocalizableString.DeviceInfo.lowBatteryWarningMarkdown return (CardWarningConfiguration(type: .warning, - message: LocalizableString.DeviceInfo.lowBatteryWarningMarkdown.localized, + message: message.localized, closeAction: nil), callback) case .ok: @@ -418,7 +497,7 @@ extension DeviceInfoViewModel.InfoField { } } - func warningForStationRssi(for rssi: Int?) -> (CardWarningConfiguration, VoidCallback)? { + func warningForStationRssi(for rssi: Int?, device: DeviceDetails) -> (CardWarningConfiguration, VoidCallback)? { guard let rssi else { return nil } @@ -430,12 +509,12 @@ extension DeviceInfoViewModel.InfoField { case _ where rssi >= -95: return (CardWarningConfiguration(type: .warning, message: LocalizableString.DeviceInfo.stationRssiWarning.localized, - linkText: LocalizableString.url(urlText, DisplayedLinks.troubleshooting.linkURL).localized, + linkText: LocalizableString.url(urlText, device.troubleShootingUrl ?? "").localized, closeAction: nil), {}) default: return (CardWarningConfiguration(type: .error, message: LocalizableString.DeviceInfo.stationRssiError.localized, - linkText: LocalizableString.url(urlText, DisplayedLinks.troubleshooting.linkURL).localized, + linkText: LocalizableString.url(urlText, device.troubleShootingUrl ?? "").localized, closeAction: nil), {}) } } @@ -482,6 +561,26 @@ extension DeviceInfoViewModel.InfoField { return nil case .stationRssi: return nil + case .gsmSignal: + return nil + case .gwFrequency: + return nil + case .externalSim: + return nil + case .iccid: + return nil + case .mobileCountryCode: + return nil + case .gwBatteryState: + return nil + case .signalGwStation: + return nil + case .nextServerCommunication: + return nil + case .stationId: + return nil + case .signalStationGw: + return nil } } } diff --git a/PresentationLayer/UIComponents/Screens/DeviceInfo/DeviceInfoViewModel+Content.swift b/PresentationLayer/UIComponents/Screens/DeviceInfo/DeviceInfoViewModel+Content.swift index c5f295e64..b5fb9d0d9 100644 --- a/PresentationLayer/UIComponents/Screens/DeviceInfo/DeviceInfoViewModel+Content.swift +++ b/PresentationLayer/UIComponents/Screens/DeviceInfo/DeviceInfoViewModel+Content.swift @@ -89,6 +89,14 @@ extension DeviceInfoViewModel { enum InfoField { case name case bundleName + case gsmSignal + case gwFrequency + case externalSim + case iccid + case mobileCountryCode + case gwBatteryState + case signalGwStation + case nextServerCommunication case devEUI case gatewayModel case hardwareVersion @@ -105,6 +113,8 @@ extension DeviceInfoViewModel { case stationModel case lastStationActivity case stationRssi + case stationId + case signalStationGw static var heliumFields: [InfoField] { [.name, .bundleName, .stationModel, .claimedAt, .devEUI, .firmwareVersion, .hardwareVersion, .batteryState, .lastHotspot, .lastRSSI, .lastStationActivity] @@ -127,5 +137,24 @@ extension DeviceInfoViewModel { static var wifiStationDetailsInfoFields: [InfoField] { [.stationModel, .hardwareVersion, .stationRssi, .batteryState, .lastStationActivity] } + + static var pulseFields: [InfoField] { + [pulseInfoFields, + pulseGatewayDetailsInfoFields, + pulseStationDetailsInfoFields].flatMap { $0 } + } + + static var pulseInfoFields: [InfoField] { + [.name, .bundleName, .claimedAt] + } + + static var pulseGatewayDetailsInfoFields: [InfoField] { + [.gatewayModel, .serialNumber, .gsmSignal, .gwFrequency, .externalSim, .iccid, .mobileCountryCode, .firmwareVersion, .gwBatteryState, .lastGatewayActivity, .signalGwStation, .nextServerCommunication] + } + + static var pulseStationDetailsInfoFields: [InfoField] { + [.stationModel, .stationId, .batteryState, .hardwareVersion, .signalStationGw, .lastStationActivity] + } + } } diff --git a/PresentationLayer/UIComponents/Screens/DeviceInfo/DeviceInfoViewModel.swift b/PresentationLayer/UIComponents/Screens/DeviceInfo/DeviceInfoViewModel.swift index 36ecc42a7..f18b7ad47 100644 --- a/PresentationLayer/UIComponents/Screens/DeviceInfo/DeviceInfoViewModel.swift +++ b/PresentationLayer/UIComponents/Screens/DeviceInfo/DeviceInfoViewModel.swift @@ -71,13 +71,21 @@ class DeviceInfoViewModel: ObservableObject { } var infoSections: [StationInfoView.Section] { - if device.isHelium { - return [getInfoSection(title: nil, fields: InfoField.heliumFields)] - } else { - let sections: [StationInfoView.Section] = [getInfoSection(title: nil, fields: InfoField.wifiInfoFields), - getInfoSection(title: LocalizableString.DeviceInfo.gatewayDetails.localized, fields: InfoField.wifiGatewayDetailsInfoFields), - getInfoSection(title: LocalizableString.DeviceInfo.stationDetails.localized, fields: InfoField.wifiStationDetailsInfoFields)] - return sections + switch device.bundle?.connectivity { + case .wifi: + let sections: [StationInfoView.Section] = [getInfoSection(title: nil, fields: InfoField.wifiInfoFields), + getInfoSection(title: LocalizableString.DeviceInfo.gatewayDetails.localized, fields: InfoField.wifiGatewayDetailsInfoFields), + getInfoSection(title: LocalizableString.DeviceInfo.stationDetails.localized, fields: InfoField.wifiStationDetailsInfoFields)] + return sections + case .helium: + return [getInfoSection(title: nil, fields: InfoField.heliumFields)] + case .cellular: + let sections: [StationInfoView.Section] = [getInfoSection(title: nil, fields: InfoField.pulseInfoFields), + getInfoSection(title: LocalizableString.DeviceInfo.gatewayDetails.localized, fields: InfoField.pulseGatewayDetailsInfoFields), + getInfoSection(title: LocalizableString.DeviceInfo.stationDetails.localized, fields: InfoField.pulseStationDetailsInfoFields)] + return sections + case nil: + return [] } } @@ -316,6 +324,26 @@ private extension DeviceInfoViewModel { break case .stationRssi: break + case .gsmSignal: + break + case .gwFrequency: + break + case .externalSim: + break + case .iccid: + break + case .mobileCountryCode: + break + case .gwBatteryState: + break + case .signalGwStation: + break + case .nextServerCommunication: + break + case .stationId: + break + case .signalStationGw: + break } } diff --git a/WeatherXMTests/PresentationLayer/Extensions/DomainExtensions/DeviceDetailsTests.swift b/WeatherXMTests/PresentationLayer/Extensions/DomainExtensions/DeviceDetailsTests.swift index 58bdf41f3..92dbaf700 100644 --- a/WeatherXMTests/PresentationLayer/Extensions/DomainExtensions/DeviceDetailsTests.swift +++ b/WeatherXMTests/PresentationLayer/Extensions/DomainExtensions/DeviceDetailsTests.swift @@ -43,7 +43,7 @@ struct DeviceDetailsTests { device.bundle = .mock(name: .d1) #expect(device.troubleShootingUrl == DisplayedLinks.d1Troubleshooting.linkURL) device.bundle = .mock(name: .pulse) - #expect(device.troubleShootingUrl == nil) + #expect(device.troubleShootingUrl == DisplayedLinks.pulseTroubleshooting.linkURL) device.bundle = nil #expect(device.troubleShootingUrl == nil) } diff --git a/wxm-ios/DataLayer/DataLayer.xcodeproj/project.pbxproj b/wxm-ios/DataLayer/DataLayer.xcodeproj/project.pbxproj index 22d9e8054..67f9a9624 100644 --- a/wxm-ios/DataLayer/DataLayer.xcodeproj/project.pbxproj +++ b/wxm-ios/DataLayer/DataLayer.xcodeproj/project.pbxproj @@ -78,6 +78,7 @@ 26DF1F542D549A310001E936 /* FileUploadMockProtocol.swift in Sources */ = {isa = PBXBuildFile; fileRef = 26DF1F532D549A310001E936 /* FileUploadMockProtocol.swift */; }; 26DF1F602D5611EA0001E936 /* post_login.json in Resources */ = {isa = PBXBuildFile; fileRef = 26DF1F5F2D5611DD0001E936 /* post_login.json */; }; 26E2B3BC2982754D00ED9B58 /* MockProtocol.swift in Sources */ = {isa = PBXBuildFile; fileRef = 26E2B3BB2982754D00ED9B58 /* MockProtocol.swift */; }; + 26E558432E8E8A9D00E834CC /* get_device_info_pulse.json in Resources */ = {isa = PBXBuildFile; fileRef = 26E558422E8E8A8D00E834CC /* get_device_info_pulse.json */; }; 26E88B2F29E5717D0023BBD5 /* DatabaseService.swift in Sources */ = {isa = PBXBuildFile; fileRef = 26E88B2E29E5717D0023BBD5 /* DatabaseService.swift */; }; 26E88B3429E582070023BBD5 /* DBWeather+CoreDataClass.swift in Sources */ = {isa = PBXBuildFile; fileRef = 26E88B3329E582070023BBD5 /* DBWeather+CoreDataClass.swift */; }; 26E88B3529E582070023BBD5 /* DBWeather+CoreDataProperties.swift in Sources */ = {isa = PBXBuildFile; fileRef = 26E88B3229E582070023BBD5 /* DBWeather+CoreDataProperties.swift */; }; @@ -183,6 +184,7 @@ 26DF1F532D549A310001E936 /* FileUploadMockProtocol.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = FileUploadMockProtocol.swift; sourceTree = ""; }; 26DF1F5F2D5611DD0001E936 /* post_login.json */ = {isa = PBXFileReference; lastKnownFileType = text.json; path = post_login.json; sourceTree = ""; }; 26E2B3BB2982754D00ED9B58 /* MockProtocol.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MockProtocol.swift; sourceTree = ""; }; + 26E558422E8E8A8D00E834CC /* get_device_info_pulse.json */ = {isa = PBXFileReference; lastKnownFileType = text.json; path = get_device_info_pulse.json; sourceTree = ""; }; 26E88B2E29E5717D0023BBD5 /* DatabaseService.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DatabaseService.swift; sourceTree = ""; }; 26E88B3229E582070023BBD5 /* DBWeather+CoreDataProperties.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "DBWeather+CoreDataProperties.swift"; sourceTree = ""; }; 26E88B3329E582070023BBD5 /* DBWeather+CoreDataClass.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "DBWeather+CoreDataClass.swift"; sourceTree = ""; }; @@ -264,6 +266,7 @@ 2618A6F02A1F61FD00983B7B /* get_device_history_24_samples.json */, 2646D8E729C8B4310000237F /* get_device_info_helium.json */, 2646D8E929C8B8CA0000237F /* get_device_info_m5.json */, + 26E558422E8E8A8D00E834CC /* get_device_info_pulse.json */, 26348E262A4456DD000846C6 /* get_network_search.json */, 26ACE8EE2C883BF400EA22DD /* get_devices_rewards_analytics.json */, 26AD3EA52C90748C0040AB09 /* get_devices_rewards_analytics_7d.json */, @@ -598,6 +601,7 @@ 2646D8EA29C8B8CA0000237F /* get_device_info_m5.json in Resources */, 26348E272A4456DD000846C6 /* get_network_search.json in Resources */, 2674017A2A386DA700E54E35 /* get_network_stats.json in Resources */, + 26E558432E8E8A9D00E834CC /* get_device_info_pulse.json in Resources */, 2674EDCE2A3C4C3800616285 /* get_network_stats_alt.json in Resources */, 264CC75D2B986F310060DEA4 /* get_reward_boosts.json in Resources */, 26AD3E9C2C8F3F4C0040AB09 /* get_device_rewards_analytics_year.json in Resources */, diff --git a/wxm-ios/DataLayer/DataLayer/Networking/ApiRequestBuilders/MeApiRequestBuilder.swift b/wxm-ios/DataLayer/DataLayer/Networking/ApiRequestBuilders/MeApiRequestBuilder.swift index 051913aed..29e32d1b6 100644 --- a/wxm-ios/DataLayer/DataLayer/Networking/ApiRequestBuilders/MeApiRequestBuilder.swift +++ b/wxm-ios/DataLayer/DataLayer/Networking/ApiRequestBuilders/MeApiRequestBuilder.swift @@ -211,7 +211,7 @@ extension MeApiRequestBuilder: MockResponseBuilder { case .getUserDeviceHistoryById: return "get_device_history" case .getUserDeviceInfoById: - return "get_device_info_m5" + return "get_device_info_pulse" case .getUserWallet: return "get_user_wallet" case .getUserDeviceForecastById: diff --git a/wxm-ios/DataLayer/DataLayer/Networking/Mock/Jsons/get_device_info_pulse.json b/wxm-ios/DataLayer/DataLayer/Networking/Mock/Jsons/get_device_info_pulse.json new file mode 100644 index 000000000..9a8f16ded --- /dev/null +++ b/wxm-ios/DataLayer/DataLayer/Networking/Mock/Jsons/get_device_info_pulse.json @@ -0,0 +1,41 @@ +{ + "name": "PCDFC94E2DAD11223", + "claimed_at": "2025-05-05T14:54:42+03:00", + "reward_split": [ + { + "wallet": "0xb4D149f047AE10f1c1D7DC9D4D0729C13A5848dd", + "stake": 100, + "reward": 0 + } + ], + "gateway": { + "model": "WG3000", + "serial_number": "CD:FC:94:E2:DA:D1:12:23", + "firmware": { + "assigned": "5.57" + }, + "gps_sats_last_activity": "2025-10-01T14:25:44+03:00", + "wifi_rssi_last_activity": "2025-10-01T14:25:44+03:00", + "network_rssi": -71, + "network_rssi_last_activity": "2025-10-01T14:25:44+03:00", + "gateway_rssi": -82, + "gateway_rssi_last_activity": "2025-10-01T14:25:44+03:00", + "last_activity": "2025-10-01T14:25:44+03:00", + "sim": { + "mcc": "202", + "mnc": "5", + "iccid": "89882280666087778828" + }, + "bat_state": "low", + "frequency": "868", + "next_communication": "2025-10-01T14:25:44+03:00" + }, + "weather_station": { + "model": "WS1001", + "id": "00002E17", + "bat_state": "ok", + "station_rssi": -90, + "station_rssi_last_activity": "2025-10-01T14:25:44+03:00", + "last_activity": "2025-10-01T12:56:13+03:00" + } +} diff --git a/wxm-ios/DomainLayer/DomainLayer/Entities/Codables/Devices/NetworkDevicesInfoResponse.swift b/wxm-ios/DomainLayer/DomainLayer/Entities/Codables/Devices/NetworkDevicesInfoResponse.swift index f14867bf5..02d7ca54a 100644 --- a/wxm-ios/DomainLayer/DomainLayer/Entities/Codables/Devices/NetworkDevicesInfoResponse.swift +++ b/wxm-ios/DomainLayer/DomainLayer/Entities/Codables/Devices/NetworkDevicesInfoResponse.swift @@ -54,6 +54,15 @@ public extension NetworkDevicesInfoResponse { public let wifiRssi: String? public let wifiRssiLastActivity: Date? public let lastActivity: Date? + public let networkRssi: Int? + public let networkRssiLastActivity: Date? + public let gatewayRssi: Int? + public let gatewayRssiLastActivity: Date? + public let batState: BatteryState? + public let frequency: String? + public let nextCommunication: Date? + public let sim: Sim? + enum CodingKeys: String, CodingKey { case model @@ -64,6 +73,14 @@ public extension NetworkDevicesInfoResponse { case gpsSatsLastActivity = "gps_sats_last_activity" case wifiRssi = "wifi_rssi" case wifiRssiLastActivity = "wifi_rssi_last_activity" + case networkRssi = "network_rssi" + case networkRssiLastActivity = "network_rssi_last_activity" + case gatewayRssi = "gateway_rssi" + case gatewayRssiLastActivity = "gateway_rssi_last_activity" + case batState = "bat_state" + case frequency + case nextCommunication = "next_communication" + case sim } public init(from decoder: Decoder) throws { @@ -76,11 +93,20 @@ public extension NetworkDevicesInfoResponse { self.wifiRssi = try container.decodeIfPresent(String.self, forKey: .wifiRssi) self.wifiRssiLastActivity = try container.decodeIfPresent(Date.self, forKey: .wifiRssiLastActivity) self.firmware = try container.decodeIfPresent(Firmware.self, forKey: .firmware) + self.networkRssi = try container.decodeIfPresent(Int.self, forKey: .networkRssi) + self.networkRssiLastActivity = try container.decodeIfPresent(Date.self, forKey: .networkRssiLastActivity) + self.gatewayRssi = try container.decodeIfPresent(Int.self, forKey: .gatewayRssi) + self.gatewayRssiLastActivity = try container.decodeIfPresent(Date.self, forKey: .gatewayRssiLastActivity) + self.batState = try container.decodeIfPresent(BatteryState.self, forKey: .batState) + self.frequency = try container.decodeIfPresent(String.self, forKey: .frequency) + self.nextCommunication = try container.decodeIfPresent(Date.self, forKey: .nextCommunication) + self.sim = try container.decodeIfPresent(Sim.self, forKey: .sim) } } struct WeatherStation: Codable, Sendable { public let model: String? + public let stationId: String? public let lastActivity: Date? public let batState: BatteryState? public let devEui: String? @@ -95,6 +121,7 @@ public extension NetworkDevicesInfoResponse { enum CodingKeys: String, CodingKey { case model + case stationId = "id" case firmware case devEui = "dev_eui" case hwVersion = "hw_version" @@ -111,6 +138,7 @@ public extension NetworkDevicesInfoResponse { public init(from decoder: Decoder) throws { let container = try decoder.container(keyedBy: CodingKeys.self) self.model = try container.decodeIfPresent(String.self, forKey: .model) + self.stationId = try container.decodeIfPresent(String.self, forKey: .stationId) self.devEui = try container.decodeIfPresent(String.self, forKey: .devEui) self.hwVersion = try container.decodeIfPresent(String.self, forKey: .hwVersion) self.firmware = try container.decodeIfPresent(Firmware.self, forKey: .firmware) diff --git a/wxm-ios/DomainLayer/DomainLayer/Entities/Codables/Devices/NetworkDevicesTypes.swift b/wxm-ios/DomainLayer/DomainLayer/Entities/Codables/Devices/NetworkDevicesTypes.swift index a017a8f34..4e2e48b46 100644 --- a/wxm-ios/DomainLayer/DomainLayer/Entities/Codables/Devices/NetworkDevicesTypes.swift +++ b/wxm-ios/DomainLayer/DomainLayer/Entities/Codables/Devices/NetworkDevicesTypes.swift @@ -108,3 +108,9 @@ public struct Metrics: Codable, Sendable { self.polReason = try? container.decodeIfPresent(PolStatus.self, forKey: .polReason) ?? .verified } } + +public struct Sim: Codable, Sendable { + public let mcc: String? + public let mnc: String? + public let iccid: String? +} diff --git a/wxm-ios/Resources/Localizable/Localizable.xcstrings b/wxm-ios/Resources/Localizable/Localizable.xcstrings index 540f0fdf6..0c0086a6b 100644 --- a/wxm-ios/Resources/Localizable/Localizable.xcstrings +++ b/wxm-ios/Resources/Localizable/Localizable.xcstrings @@ -2871,6 +2871,17 @@ } } }, + "device_info_external_sim" : { + "extractionState" : "manual", + "localizations" : { + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "External SIM" + } + } + } + }, "device_info_followed_contact_support_title" : { "extractionState" : "manual", "localizations" : { @@ -2893,6 +2904,61 @@ } } }, + "device_info_gateway_low_battery_warning_markdown" : { + "extractionState" : "manual", + "localizations" : { + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "We’ve detected that your gateway’s battery level is **Low**.\nPlease replace the batteries as soon as possible, as **this may affect your WXM rewards and data**." + } + } + } + }, + "device_info_gsm_signal" : { + "extractionState" : "manual", + "localizations" : { + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "GSM Signal" + } + } + } + }, + "device_info_gw_battery_state" : { + "extractionState" : "manual", + "localizations" : { + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "Gateway Battery Level" + } + } + } + }, + "device_info_gw_frequency" : { + "extractionState" : "manual", + "localizations" : { + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "Gateway frequency" + } + } + } + }, + "device_info_iccid" : { + "extractionState" : "manual", + "localizations" : { + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "ICCID of external SIM" + } + } + } + }, "device_info_invalid_friendly_name" : { "extractionState" : "manual", "localizations" : { @@ -2904,6 +2970,17 @@ } } }, + "device_info_is_in_use" : { + "extractionState" : "manual", + "localizations" : { + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "Is in use" + } + } + } + }, "device_info_last_gateway_activity" : { "extractionState" : "manual", "localizations" : { @@ -2937,6 +3014,39 @@ } } }, + "device_info_mobile_country_code" : { + "extractionState" : "manual", + "localizations" : { + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "Mobile country and network code" + } + } + } + }, + "device_info_mobile_country_network_code" : { + "extractionState" : "manual", + "localizations" : { + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "MCC: %@ - MNC: %@" + } + } + } + }, + "device_info_next_server_communication" : { + "extractionState" : "manual", + "localizations" : { + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "Next server communication " + } + } + } + }, "device_info_owned_station_location_description_format" : { "extractionState" : "manual", "localizations" : { @@ -3014,6 +3124,28 @@ } } }, + "device_info_signal_gw_station" : { + "extractionState" : "manual", + "localizations" : { + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "Signal between Gateway & Station (RSSI)" + } + } + } + }, + "device_info_signal_station_gw" : { + "extractionState" : "manual", + "localizations" : { + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "Signal between Station & Gateway (RSSI)" + } + } + } + }, "device_info_station_back_to_settings" : { "extractionState" : "manual", "localizations" : { @@ -3102,6 +3234,17 @@ } } }, + "device_info_station_id" : { + "extractionState" : "manual", + "localizations" : { + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "ID" + } + } + } + }, "device_info_station_info_ATECC" : { "extractionState" : "manual", "localizations" : { diff --git a/wxm-ios/Resources/Localizable/LocalizableString+DeviceInfo.swift b/wxm-ios/Resources/Localizable/LocalizableString+DeviceInfo.swift index 4dc1eb3a6..5173f6bc1 100644 --- a/wxm-ios/Resources/Localizable/LocalizableString+DeviceInfo.swift +++ b/wxm-ios/Resources/Localizable/LocalizableString+DeviceInfo.swift @@ -64,6 +64,7 @@ extension LocalizableString { case stationFrequencyChangeFailed case stationFrequencyChangeFailureDescription(String) case lowBatteryWarningMarkdown + case gatewayLowBatteryWarningMarkdown case removeStationAccountConfirmationMarkdown case gatewayDetails case stationDetails @@ -74,6 +75,18 @@ extension LocalizableString { case photoVerificationStartButtonTitle case photoVerificationUploadingDescription case photoVerificationWithPhotosDescription + case gsmSignal + case gwFrequency + case externalSim + case iccid + case mobileCountryCode + case gwBatteryState + case signalGwStation + case nextServerCommunication + case stationId + case signalStationGw + case mobileCountryNetworkCode(String, String) + case isInUse } } @@ -92,6 +105,8 @@ extension LocalizableString.DeviceInfo: WXMLocalizable { .stationFrequencyChangeFailureDescription(let text), .satellites(let text): localized = String(format: localized, text) + case .mobileCountryNetworkCode(let text0, let text1): + localized = String(format: localized, text0, text1) default: break } @@ -210,6 +225,8 @@ extension LocalizableString.DeviceInfo: WXMLocalizable { return "device_info_station_frequency_change_failure_description_format" case .lowBatteryWarningMarkdown: return "device_info_low_battery_warning_markdown" + case .gatewayLowBatteryWarningMarkdown: + return "device_info_gateway_low_battery_warning_markdown" case .removeStationAccountConfirmationMarkdown: return "device_info_remove_station_account_confirmation_markdown" case .gatewayDetails: @@ -230,6 +247,30 @@ extension LocalizableString.DeviceInfo: WXMLocalizable { return "device_info_photo_verification_uploading_description" case .photoVerificationWithPhotosDescription: return "device_info_photo_verification_with_photos_description" + case .gsmSignal: + return "device_info_gsm_signal" + case .gwFrequency: + return "device_info_gw_frequency" + case .externalSim: + return "device_info_external_sim" + case .iccid: + return "device_info_iccid" + case .mobileCountryCode: + return "device_info_mobile_country_code" + case .gwBatteryState: + return "device_info_gw_battery_state" + case .signalGwStation: + return "device_info_signal_gw_station" + case .nextServerCommunication: + return "device_info_next_server_communication" + case .stationId: + return "device_info_station_id" + case .signalStationGw: + return "device_info_signal_station_gw" + case .mobileCountryNetworkCode: + return "device_info_mobile_country_network_code" + case .isInUse: + return "device_info_is_in_use" } } } diff --git a/wxm-ios/Resources/Localizable/LocalizableString+Units.swift b/wxm-ios/Resources/Localizable/LocalizableString+Units.swift index c1a710571..68167aa87 100644 --- a/wxm-ios/Resources/Localizable/LocalizableString+Units.swift +++ b/wxm-ios/Resources/Localizable/LocalizableString+Units.swift @@ -40,7 +40,7 @@ extension LocalizableString { case inchesPerHourSymbol case wattsPerSquareSymbol case dBmSymbol - + case mhz } } @@ -114,6 +114,8 @@ extension LocalizableString.Units: WXMLocalizable { "units_watts_per_square_symbol" case .dBmSymbol: "units_dbm_symbol" + case .mhz: + "units_mhz" } } } diff --git a/wxm-ios/Resources/Localizable/LocalizableUnits.xcstrings b/wxm-ios/Resources/Localizable/LocalizableUnits.xcstrings index 93ecad4fe..bde92d8b9 100644 --- a/wxm-ios/Resources/Localizable/LocalizableUnits.xcstrings +++ b/wxm-ios/Resources/Localizable/LocalizableUnits.xcstrings @@ -265,6 +265,17 @@ } } }, + "units_mhz" : { + "extractionState" : "manual", + "localizations" : { + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "MHz" + } + } + } + }, "units_miles_per_hour_friendly_name" : { "extractionState" : "manual", "localizations" : {