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 */; };
|
||||
36C2ECDE207C82DE003CDA33 /* ItemsParsing.swift in Sources */ = {isa = PBXBuildFile; fileRef = 36C2ECDA207C3FE7003CDA33 /* ItemsParsing.swift */; };
|
||||
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 */; };
|
||||
6027D1B92080E52A004FFDC7 /* BrightnessViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6027D1B72080E52A004FFDC7 /* BrightnessViewController.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>"; };
|
||||
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>"; };
|
||||
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>"; };
|
||||
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>"; };
|
||||
@ -294,6 +296,7 @@
|
||||
36C2ECD6207B6DAE003CDA33 /* TimeTouchBarItem.swift */,
|
||||
6027D1B82080E52A004FFDC7 /* VolumeViewController.swift */,
|
||||
607EEA4A2087835F009DA5F0 /* WeatherBarItem.swift */,
|
||||
4CFF5E5B22E623DD00BFB1EE /* YandexWeatherBarItem.swift */,
|
||||
B08126F0217BE19000A98970 /* WidgetProtocol.swift */,
|
||||
B0F54A792295AC7D00B4C509 /* DarkModeBarItem.swift */,
|
||||
);
|
||||
@ -458,6 +461,7 @@
|
||||
6027D1BA2080E52A004FFDC7 /* VolumeViewController.swift in Sources */,
|
||||
607EEA4B2087835F009DA5F0 /* WeatherBarItem.swift in Sources */,
|
||||
B0F3112520C9E35F0076BB88 /* SupportNSTouchBar.swift in Sources */,
|
||||
4CFF5E5C22E623DD00BFB1EE /* YandexWeatherBarItem.swift in Sources */,
|
||||
6042B6AA2083E27000C525C8 /* DeprecatedCarbonAPI.c in Sources */,
|
||||
B09EB1E4207C082000D5C1E0 /* HapticFeedback.swift in Sources */,
|
||||
B08173272135F02B005D4908 /* NightShiftBarItem.swift in Sources */,
|
||||
|
||||
@ -179,6 +179,20 @@ class SupportedTypesHolder {
|
||||
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
|
||||
enum CodingKeys: String, CodingKey { case refreshInterval; case from; case to; case full }
|
||||
@ -335,6 +349,7 @@ enum ItemType: Decodable {
|
||||
case volume()
|
||||
case brightness(refreshInterval: Double)
|
||||
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 inputsource()
|
||||
case music(interval: Double, disableMarquee: Bool)
|
||||
@ -378,6 +393,7 @@ enum ItemType: Decodable {
|
||||
case volume
|
||||
case brightness
|
||||
case weather
|
||||
case yandexWeather
|
||||
case currency
|
||||
case inputsource
|
||||
case music
|
||||
@ -427,6 +443,10 @@ enum ItemType: Decodable {
|
||||
let api_key = try container.decodeIfPresent(String.self, forKey: .api_key) ?? "32c4256d09a4c52b38aecddba7a078f6"
|
||||
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)
|
||||
|
||||
case .yandexWeather:
|
||||
let interval = try container.decodeIfPresent(Double.self, forKey: .refreshInterval) ?? 1800.0
|
||||
self = .yandexWeather(interval: interval)
|
||||
|
||||
case .currency:
|
||||
let interval = try container.decodeIfPresent(Double.self, forKey: .refreshInterval) ?? 600.0
|
||||
|
||||
@ -35,6 +35,8 @@ extension ItemType {
|
||||
return "com.toxblh.mtmr.brightness"
|
||||
case .weather(interval: _, units: _, api_key: _, icon_type: _):
|
||||
return "com.toxblh.mtmr.weather"
|
||||
case .yandexWeather(interval: _):
|
||||
return "com.toxblh.mtmr.yandexWeather"
|
||||
case .currency(interval: _, from: _, to: _, full: _):
|
||||
return "com.toxblh.mtmr.currency"
|
||||
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):
|
||||
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):
|
||||
barItem = CurrencyBarItem(identifier: identifier, interval: interval, from: from, to: to, full: full)
|
||||
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
|
||||
```
|
||||
|
||||
#### `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`
|
||||
|
||||
> Provider: https://coinbase.com
|
||||
|
||||
Loading…
Reference in New Issue
Block a user