mirror of
https://github.com/Toxblh/MTMR.git
synced 2026-01-10 17:08:39 +00:00
Add new "yandexWeather" widget
This commit is contained in:
parent
000b825ec9
commit
6a85bea5b5
@ -18,6 +18,7 @@
|
|||||||
36C2ECDD207C723B003CDA33 /* ParseConfigTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 36C2ECDC207C723B003CDA33 /* ParseConfigTests.swift */; };
|
36C2ECDD207C723B003CDA33 /* ParseConfigTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 36C2ECDC207C723B003CDA33 /* ParseConfigTests.swift */; };
|
||||||
36C2ECDE207C82DE003CDA33 /* ItemsParsing.swift in Sources */ = {isa = PBXBuildFile; fileRef = 36C2ECDA207C3FE7003CDA33 /* ItemsParsing.swift */; };
|
36C2ECDE207C82DE003CDA33 /* ItemsParsing.swift in Sources */ = {isa = PBXBuildFile; fileRef = 36C2ECDA207C3FE7003CDA33 /* ItemsParsing.swift */; };
|
||||||
36C2ECE0207CB1B0003CDA33 /* defaultPreset.json in Resources */ = {isa = PBXBuildFile; fileRef = 36C2ECDF207CB1B0003CDA33 /* defaultPreset.json */; };
|
36C2ECE0207CB1B0003CDA33 /* defaultPreset.json in Resources */ = {isa = PBXBuildFile; fileRef = 36C2ECDF207CB1B0003CDA33 /* defaultPreset.json */; };
|
||||||
|
4CFF5E5C22E623DD00BFB1EE /* YandexWeatherBarItem.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4CFF5E5B22E623DD00BFB1EE /* YandexWeatherBarItem.swift */; };
|
||||||
60173D3E20C0031B002C305F /* LaunchAtLoginController.m in Sources */ = {isa = PBXBuildFile; fileRef = 60173D3D20C0031B002C305F /* LaunchAtLoginController.m */; };
|
60173D3E20C0031B002C305F /* LaunchAtLoginController.m in Sources */ = {isa = PBXBuildFile; fileRef = 60173D3D20C0031B002C305F /* LaunchAtLoginController.m */; };
|
||||||
6027D1B92080E52A004FFDC7 /* BrightnessViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6027D1B72080E52A004FFDC7 /* BrightnessViewController.swift */; };
|
6027D1B92080E52A004FFDC7 /* BrightnessViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6027D1B72080E52A004FFDC7 /* BrightnessViewController.swift */; };
|
||||||
6027D1BA2080E52A004FFDC7 /* VolumeViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6027D1B82080E52A004FFDC7 /* VolumeViewController.swift */; };
|
6027D1BA2080E52A004FFDC7 /* VolumeViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6027D1B82080E52A004FFDC7 /* VolumeViewController.swift */; };
|
||||||
@ -91,6 +92,7 @@
|
|||||||
36C2ECDA207C3FE7003CDA33 /* ItemsParsing.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ItemsParsing.swift; sourceTree = "<group>"; };
|
36C2ECDA207C3FE7003CDA33 /* ItemsParsing.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ItemsParsing.swift; sourceTree = "<group>"; };
|
||||||
36C2ECDC207C723B003CDA33 /* ParseConfigTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ParseConfigTests.swift; sourceTree = "<group>"; };
|
36C2ECDC207C723B003CDA33 /* ParseConfigTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ParseConfigTests.swift; sourceTree = "<group>"; };
|
||||||
36C2ECDF207CB1B0003CDA33 /* defaultPreset.json */ = {isa = PBXFileReference; lastKnownFileType = text.json; path = defaultPreset.json; sourceTree = "<group>"; };
|
36C2ECDF207CB1B0003CDA33 /* defaultPreset.json */ = {isa = PBXFileReference; lastKnownFileType = text.json; path = defaultPreset.json; sourceTree = "<group>"; };
|
||||||
|
4CFF5E5B22E623DD00BFB1EE /* YandexWeatherBarItem.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = YandexWeatherBarItem.swift; sourceTree = "<group>"; };
|
||||||
60173D3C20C0031B002C305F /* LaunchAtLoginController.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = LaunchAtLoginController.h; sourceTree = "<group>"; };
|
60173D3C20C0031B002C305F /* LaunchAtLoginController.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = LaunchAtLoginController.h; sourceTree = "<group>"; };
|
||||||
60173D3D20C0031B002C305F /* LaunchAtLoginController.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = LaunchAtLoginController.m; sourceTree = "<group>"; };
|
60173D3D20C0031B002C305F /* LaunchAtLoginController.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = LaunchAtLoginController.m; sourceTree = "<group>"; };
|
||||||
6027D1B72080E52A004FFDC7 /* BrightnessViewController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = BrightnessViewController.swift; sourceTree = "<group>"; };
|
6027D1B72080E52A004FFDC7 /* BrightnessViewController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = BrightnessViewController.swift; sourceTree = "<group>"; };
|
||||||
@ -294,6 +296,7 @@
|
|||||||
36C2ECD6207B6DAE003CDA33 /* TimeTouchBarItem.swift */,
|
36C2ECD6207B6DAE003CDA33 /* TimeTouchBarItem.swift */,
|
||||||
6027D1B82080E52A004FFDC7 /* VolumeViewController.swift */,
|
6027D1B82080E52A004FFDC7 /* VolumeViewController.swift */,
|
||||||
607EEA4A2087835F009DA5F0 /* WeatherBarItem.swift */,
|
607EEA4A2087835F009DA5F0 /* WeatherBarItem.swift */,
|
||||||
|
4CFF5E5B22E623DD00BFB1EE /* YandexWeatherBarItem.swift */,
|
||||||
B08126F0217BE19000A98970 /* WidgetProtocol.swift */,
|
B08126F0217BE19000A98970 /* WidgetProtocol.swift */,
|
||||||
B0F54A792295AC7D00B4C509 /* DarkModeBarItem.swift */,
|
B0F54A792295AC7D00B4C509 /* DarkModeBarItem.swift */,
|
||||||
);
|
);
|
||||||
@ -458,6 +461,7 @@
|
|||||||
6027D1BA2080E52A004FFDC7 /* VolumeViewController.swift in Sources */,
|
6027D1BA2080E52A004FFDC7 /* VolumeViewController.swift in Sources */,
|
||||||
607EEA4B2087835F009DA5F0 /* WeatherBarItem.swift in Sources */,
|
607EEA4B2087835F009DA5F0 /* WeatherBarItem.swift in Sources */,
|
||||||
B0F3112520C9E35F0076BB88 /* SupportNSTouchBar.swift in Sources */,
|
B0F3112520C9E35F0076BB88 /* SupportNSTouchBar.swift in Sources */,
|
||||||
|
4CFF5E5C22E623DD00BFB1EE /* YandexWeatherBarItem.swift in Sources */,
|
||||||
6042B6AA2083E27000C525C8 /* DeprecatedCarbonAPI.c in Sources */,
|
6042B6AA2083E27000C525C8 /* DeprecatedCarbonAPI.c in Sources */,
|
||||||
B09EB1E4207C082000D5C1E0 /* HapticFeedback.swift in Sources */,
|
B09EB1E4207C082000D5C1E0 /* HapticFeedback.swift in Sources */,
|
||||||
B08173272135F02B005D4908 /* NightShiftBarItem.swift in Sources */,
|
B08173272135F02B005D4908 /* NightShiftBarItem.swift in Sources */,
|
||||||
|
|||||||
@ -179,6 +179,20 @@ class SupportedTypesHolder {
|
|||||||
parameters: [:]
|
parameters: [:]
|
||||||
)
|
)
|
||||||
},
|
},
|
||||||
|
|
||||||
|
"yandexWeather": { decoder in
|
||||||
|
enum CodingKeys: String, CodingKey { case refreshInterval }
|
||||||
|
let container = try decoder.container(keyedBy: CodingKeys.self)
|
||||||
|
let interval = try container.decodeIfPresent(Double.self, forKey: .refreshInterval)
|
||||||
|
let action = try ActionType(from: decoder)
|
||||||
|
let longAction = try LongActionType(from: decoder)
|
||||||
|
return (
|
||||||
|
item: .yandexWeather(interval: interval ?? 1800.00),
|
||||||
|
action,
|
||||||
|
longAction,
|
||||||
|
parameters: [:]
|
||||||
|
)
|
||||||
|
},
|
||||||
|
|
||||||
"currency": { decoder in
|
"currency": { decoder in
|
||||||
enum CodingKeys: String, CodingKey { case refreshInterval; case from; case to; case full }
|
enum CodingKeys: String, CodingKey { case refreshInterval; case from; case to; case full }
|
||||||
@ -335,6 +349,7 @@ enum ItemType: Decodable {
|
|||||||
case volume()
|
case volume()
|
||||||
case brightness(refreshInterval: Double)
|
case brightness(refreshInterval: Double)
|
||||||
case weather(interval: Double, units: String, api_key: String, icon_type: String)
|
case weather(interval: Double, units: String, api_key: String, icon_type: String)
|
||||||
|
case yandexWeather(interval: Double)
|
||||||
case currency(interval: Double, from: String, to: String, full: Bool)
|
case currency(interval: Double, from: String, to: String, full: Bool)
|
||||||
case inputsource()
|
case inputsource()
|
||||||
case music(interval: Double, disableMarquee: Bool)
|
case music(interval: Double, disableMarquee: Bool)
|
||||||
@ -378,6 +393,7 @@ enum ItemType: Decodable {
|
|||||||
case volume
|
case volume
|
||||||
case brightness
|
case brightness
|
||||||
case weather
|
case weather
|
||||||
|
case yandexWeather
|
||||||
case currency
|
case currency
|
||||||
case inputsource
|
case inputsource
|
||||||
case music
|
case music
|
||||||
@ -427,6 +443,10 @@ enum ItemType: Decodable {
|
|||||||
let api_key = try container.decodeIfPresent(String.self, forKey: .api_key) ?? "32c4256d09a4c52b38aecddba7a078f6"
|
let api_key = try container.decodeIfPresent(String.self, forKey: .api_key) ?? "32c4256d09a4c52b38aecddba7a078f6"
|
||||||
let icon_type = try container.decodeIfPresent(String.self, forKey: .icon_type) ?? "text"
|
let icon_type = try container.decodeIfPresent(String.self, forKey: .icon_type) ?? "text"
|
||||||
self = .weather(interval: interval, units: units, api_key: api_key, icon_type: icon_type)
|
self = .weather(interval: interval, units: units, api_key: api_key, icon_type: icon_type)
|
||||||
|
|
||||||
|
case .yandexWeather:
|
||||||
|
let interval = try container.decodeIfPresent(Double.self, forKey: .refreshInterval) ?? 1800.0
|
||||||
|
self = .yandexWeather(interval: interval)
|
||||||
|
|
||||||
case .currency:
|
case .currency:
|
||||||
let interval = try container.decodeIfPresent(Double.self, forKey: .refreshInterval) ?? 600.0
|
let interval = try container.decodeIfPresent(Double.self, forKey: .refreshInterval) ?? 600.0
|
||||||
|
|||||||
@ -35,6 +35,8 @@ extension ItemType {
|
|||||||
return "com.toxblh.mtmr.brightness"
|
return "com.toxblh.mtmr.brightness"
|
||||||
case .weather(interval: _, units: _, api_key: _, icon_type: _):
|
case .weather(interval: _, units: _, api_key: _, icon_type: _):
|
||||||
return "com.toxblh.mtmr.weather"
|
return "com.toxblh.mtmr.weather"
|
||||||
|
case .yandexWeather(interval: _):
|
||||||
|
return "com.toxblh.mtmr.yandexWeather"
|
||||||
case .currency(interval: _, from: _, to: _, full: _):
|
case .currency(interval: _, from: _, to: _, full: _):
|
||||||
return "com.toxblh.mtmr.currency"
|
return "com.toxblh.mtmr.currency"
|
||||||
case .inputsource():
|
case .inputsource():
|
||||||
@ -269,6 +271,8 @@ class TouchBarController: NSObject, NSTouchBarDelegate {
|
|||||||
}
|
}
|
||||||
case let .weather(interval: interval, units: units, api_key: api_key, icon_type: icon_type):
|
case let .weather(interval: interval, units: units, api_key: api_key, icon_type: icon_type):
|
||||||
barItem = WeatherBarItem(identifier: identifier, interval: interval, units: units, api_key: api_key, icon_type: icon_type)
|
barItem = WeatherBarItem(identifier: identifier, interval: interval, units: units, api_key: api_key, icon_type: icon_type)
|
||||||
|
case let .yandexWeather(interval: interval):
|
||||||
|
barItem = YandexWeatherBarItem(identifier: identifier, interval: interval)
|
||||||
case let .currency(interval: interval, from: from, to: to, full: full):
|
case let .currency(interval: interval, from: from, to: to, full: full):
|
||||||
barItem = CurrencyBarItem(identifier: identifier, interval: interval, from: from, to: to, full: full)
|
barItem = CurrencyBarItem(identifier: identifier, interval: interval, from: from, to: to, full: full)
|
||||||
case .inputsource():
|
case .inputsource():
|
||||||
|
|||||||
145
MTMR/Widgets/YandexWeatherBarItem.swift
Normal file
145
MTMR/Widgets/YandexWeatherBarItem.swift
Normal file
@ -0,0 +1,145 @@
|
|||||||
|
//
|
||||||
|
// YandexWeatherBarItem.swift
|
||||||
|
// MTMR
|
||||||
|
//
|
||||||
|
// Created by bobrosoft on 22/07/2019.
|
||||||
|
// Copyright © 2018 Anton Palgunov. All rights reserved.
|
||||||
|
//
|
||||||
|
|
||||||
|
import Cocoa
|
||||||
|
import CoreLocation
|
||||||
|
|
||||||
|
class YandexWeatherBarItem: CustomButtonTouchBarItem, CLLocationManagerDelegate {
|
||||||
|
private let activity: NSBackgroundActivityScheduler
|
||||||
|
private let unitsStr = "°C"
|
||||||
|
private let iconsSource = ["Ясно": "☀️", "Малооблачно": "🌤", "Облачно с прояснениями": "⛅️", "Пасмурно": "☁️", "Небольшой дождь": "🌦", "Дождь": "🌧", "Ливень": "⛈", "Гроза": "🌩", "Небольшой снег": "❄️", "Снег": "🌨", "Туман": "🌫"]
|
||||||
|
private var location: CLLocation!
|
||||||
|
private var prevLocation: CLLocation!
|
||||||
|
private var manager: CLLocationManager!
|
||||||
|
|
||||||
|
init(identifier: NSTouchBarItem.Identifier, interval: TimeInterval) {
|
||||||
|
activity = NSBackgroundActivityScheduler(identifier: "\(identifier.rawValue).updatecheck")
|
||||||
|
activity.interval = interval
|
||||||
|
|
||||||
|
super.init(identifier: identifier, title: "⏳")
|
||||||
|
|
||||||
|
let status = CLLocationManager.authorizationStatus()
|
||||||
|
if status == .restricted || status == .denied {
|
||||||
|
print("User permission not given")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
if !CLLocationManager.locationServicesEnabled() {
|
||||||
|
print("Location services not enabled")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
activity.repeats = true
|
||||||
|
activity.qualityOfService = .utility
|
||||||
|
activity.schedule { (completion: NSBackgroundActivityScheduler.CompletionHandler) in
|
||||||
|
self.updateWeather()
|
||||||
|
completion(NSBackgroundActivityScheduler.Result.finished)
|
||||||
|
}
|
||||||
|
updateWeather()
|
||||||
|
|
||||||
|
manager = CLLocationManager()
|
||||||
|
manager.delegate = self
|
||||||
|
manager.desiredAccuracy = kCLLocationAccuracyHundredMeters
|
||||||
|
manager.startUpdatingLocation()
|
||||||
|
|
||||||
|
tapClosure = tapClosure ?? defaultTapAction
|
||||||
|
}
|
||||||
|
|
||||||
|
required init?(coder _: NSCoder) {
|
||||||
|
fatalError("init(coder:) has not been implemented")
|
||||||
|
}
|
||||||
|
|
||||||
|
@objc func updateWeather() {
|
||||||
|
var urlRequest = URLRequest(url: URL(string: getWeatherUrl())!)
|
||||||
|
urlRequest.addValue("Mozilla/5.0 (Macintosh; Intel Mac OS X 10_14_4) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/75.0.3770.100 Safari/537.36", forHTTPHeaderField: "user-agent") // important for the right format
|
||||||
|
|
||||||
|
let task = URLSession.shared.dataTask(with: urlRequest) { data, _, error in
|
||||||
|
guard error == nil, let response = data?.utf8string else {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
// print(response)
|
||||||
|
|
||||||
|
var matches: [[String]]
|
||||||
|
var temperature: String?
|
||||||
|
matches = response.matchingStrings(regex: "fact__temp.*?temp__value.*?>(.*?)<")
|
||||||
|
temperature = matches.first?.item(at: 1)
|
||||||
|
|
||||||
|
var icon: String?
|
||||||
|
matches = response.matchingStrings(regex: "link__condition.*?>(.*?)<")
|
||||||
|
icon = matches.first?.item(at: 1)
|
||||||
|
if let _ = icon, let test = self.iconsSource[icon!] {
|
||||||
|
icon = test
|
||||||
|
}
|
||||||
|
|
||||||
|
if temperature != nil {
|
||||||
|
DispatchQueue.main.async {
|
||||||
|
self.setWeather(text: "\(icon ?? "?") \(temperature!)\(self.unitsStr)")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
task.resume()
|
||||||
|
}
|
||||||
|
|
||||||
|
func getWeatherUrl() -> String {
|
||||||
|
if location != nil {
|
||||||
|
return "https://yandex.ru/pogoda/?lat=\(location.coordinate.latitude)&lon=\(location.coordinate.longitude)"
|
||||||
|
} else {
|
||||||
|
return "https://yandex.ru/pogoda/" // Yandex will try to determine your location by default
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func setWeather(text: String) {
|
||||||
|
title = text
|
||||||
|
}
|
||||||
|
|
||||||
|
func defaultTapAction() {
|
||||||
|
print(getWeatherUrl())
|
||||||
|
if let url = URL(string: getWeatherUrl()) {
|
||||||
|
NSWorkspace.shared.open(url)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func locationManager(_: CLLocationManager, didUpdateLocations locations: [CLLocation]) {
|
||||||
|
let lastLocation = locations.last!
|
||||||
|
location = lastLocation
|
||||||
|
if prevLocation == nil {
|
||||||
|
updateWeather()
|
||||||
|
}
|
||||||
|
prevLocation = lastLocation
|
||||||
|
}
|
||||||
|
|
||||||
|
func locationManager(_: CLLocationManager, didFailWithError error: Error) {
|
||||||
|
print(error)
|
||||||
|
}
|
||||||
|
|
||||||
|
func locationManager(_: CLLocationManager, didChangeAuthorization _: CLAuthorizationStatus) {
|
||||||
|
updateWeather()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
extension String {
|
||||||
|
func matchingStrings(regex: String) -> [[String]] {
|
||||||
|
guard let regex = try? NSRegularExpression(pattern: regex, options: []) else { return [] }
|
||||||
|
let nsString = self as NSString
|
||||||
|
let results = regex.matches(in: self, options: [], range: NSMakeRange(0, nsString.length))
|
||||||
|
return results.map { result in
|
||||||
|
(0..<result.numberOfRanges).map {
|
||||||
|
result.range(at: $0).location != NSNotFound
|
||||||
|
? nsString.substring(with: result.range(at: $0))
|
||||||
|
: ""
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
extension Array {
|
||||||
|
func item(at index: Int) -> Element? {
|
||||||
|
return indices.contains(index) ? self[index] : nil
|
||||||
|
}
|
||||||
|
}
|
||||||
10
README.md
10
README.md
@ -188,6 +188,16 @@ To close a group, use the button:
|
|||||||
"api_key": "" // you can get the key on openweather
|
"api_key": "" // you can get the key on openweather
|
||||||
```
|
```
|
||||||
|
|
||||||
|
#### `yandexWeather`
|
||||||
|
|
||||||
|
> Provider: https://yandex.ru/pogoda. One click to open up weather forecast in your browser. \
|
||||||
|
> Note: you need to allow using "Location Services" in your Mac OS "Security & Privacy" settings for MTMR
|
||||||
|
|
||||||
|
```js
|
||||||
|
"type": "yandexWeather",
|
||||||
|
"refreshInterval": 600 // in seconds
|
||||||
|
```
|
||||||
|
|
||||||
#### `currency`
|
#### `currency`
|
||||||
|
|
||||||
> Provider: https://coinbase.com
|
> Provider: https://coinbase.com
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user