Archive for the 'Reminders' Category

2016/10/17 リマインダーで指定のリスト内の終わっていない項目をテキストで返す

リマインダー(Reminders.app)の指定名称のリスト内で、まだ終わっていない(completedがfalseな)項目をテキストで返すAppleScriptです。

リマインダーでさまざまなToDo管理を行っており、その一環としてマンガの発売予定日をまとめたリストがあるのですが、

rem1_resized.png

項目を選んでコピーし、普通にテキストエディタ(ここではmiを利用)にペーストすると、

rem2_resized.png

のようになります。親切というか、出来過ぎというか、不必要な情報まで入ってくるので、もっとシンプルに名称だけ取得してくれたほうが使い勝手がいいと思います(個人の意見です)。

そこで、さくっとAppleScriptを組んで件名(name)をテキストとして連結して取得できるようにしてみました。

AppleScript名:リマインダーで指定のリスト内の終わっていない項目をテキストで返す
– Created 2015-10-14 by Takaaki Naganoya
– 2015 Piyomaru Software
use AppleScript version “2.4″
use scripting additions
use framework “Foundation”
–http://piyocast.com/as/archives/4278

tell application “Reminders”
  tell list “マンガの発売予定日”
    set rList to name of every reminder whose completed is false
    
–リマインド項目は以下のように返ってくる
    
–>{{due date:date “2016年10月19日水曜日 12:00:00″, modification date:date “2016年10月5日水曜日 11:08:23″, class:reminder, body:missing value, completed:false, id:”x-apple-reminder://05A1CBEE-0737-47A7-ABB9-9AF582A604AF”, creation date:date “2016年10月5日水曜日 11:05:36″, name:”87 Clockers 9巻 2016/10/19″, completion date:missing value, container:list id “FA4B1A00-E1AD-4E81-921F-FDCBC7191FA3″ of application “Reminders”, priority:0, remind me date:date “2016年10月19日水曜日 12:00:00″}…. }
  end tell
end tell

set aRes to retStrFromArrayWithDelimiter(rList, return) of me
–>
(*
“87 Clockers 9巻 2016/10/19
マリアージュ 神の雫最終章 5巻 2016/10/21
女騎士、経理になる。 3巻 2016/10/24
王様達のヴァイキング 11巻 2016/10/28
アオイホノオ 16巻 2016/11/12
ピアノのムシ 9巻 2016/11/16
機動戦士Zガンダム Define 12巻 2016/11/19
MIX 10巻 2016/12/8
大砲とスタンプ 6巻 2016/12/21
スティーブス 6巻 2017/1/13
甘城ブリリアントパーク 9巻 2017/2/15
宇宙兄弟 30巻 2017/2/24
ヒーローカンパニー 10巻 2017/6/6″
*)

–リストを指定デリミタをはさんでテキスト化
on retStrFromArrayWithDelimiter(aList, aDelim)
  set anArray to current application’s NSArray’s arrayWithArray:aList
  
set aRes to anArray’s componentsJoinedByString:aDelim
  
return aRes as text
end retStrFromArrayWithDelimiter

★Click Here to Open This Script 

2015/09/25 Remindersのイベントに場所(geofence alarm)を追加する(OS X 10.11版)

Reminders.app(日本語名:リマインダー)の指定イベントに場所(geofence alarm)を追加するAppleScriptのEl Capitan(OS X 10.11)版です。

Yosemite(OS X 10.10.x)のScript Editor上では実行はおろか構文確認(コンパイル)もできないのでご注意ください。

El Capitanのリリースノートで「バグ修正」項目として紹介されている内容の1つで・・・Cocoaの各APIでの定数をAppleScriptにBridgeするようになった、という項目のテスト用にOS X 10.11上で一部修正してみました。

OS X 10.10.x上では、こうした定数があったら、

enums.png

最初の項目が0で、次が1で・・・とあたりをつけて数値で指定するとか、Xcode上でヘッダーファイルを確認して実際の値を調べる・・・という不毛な作業が必要だったわけですが、それがEl Capitanでは解消されるということです。

ただ、目下AppleScriptのプログラムリストの中で、「変数およびサブルーチン名」として色分けされる項目が、とくにCocoaの機能を使うようになってからというもの増えすぎており、可読性が落ちているので、なんとかしてほしいところです。

ちなみに、ASObjC Explorer 4ではこの問題を軽減しており、「サブルーチン名」と「Cocoaのメソッド名」については変数名とは色分けするようになっています(El Capitan上で比較のためにスクリーンショットを撮りましたが、掲載してはいけないことに気づいて削除)。

AppleScript名:Remindersのイベントに場所(geofence alarm)を追加する_10.11
– Created 2014-12-08 by Shane Stanley
– Modified 2015-09-24 by Takaaki Naganoya –Geofence Alarm
– Modified 2015-09-24 by Shane Stanley –Fix the way to Create the geofence alarm
– Modified 2015-09-24 by Takaaki Naganoya –change and test for El Capitan’s Enum bridging

–Reference:
–http://stackoverflow.com/questions/26903847/add-location-to-ekevent-ios-calendar
–http://timhibbard.com/blog/2013/01/03/how-to-create-remove-and-manage-geofence-reminders-in-ios-programmatically-with-xcode/

use AppleScript version “2.5″
use scripting additions
use framework “Foundation”
use framework “EventKit”

–Fetch an event to add geofence alarm
tell application “Reminders”
  tell account “iCloud”
    tell list “ホーム” –Use “Home” in English environment or your favorite one
      set theID to id of (last reminder whose completed is false)
    end tell
  end tell
end tell

set theID to text 20 thru -1 of theID

setLocationToLeaveForReminderID(35.745769, 139.675565, “ゲームシティ板橋”, theID, 200)

–指定場所に到着した時に発動するGeofence Alarmを指定のリマインダーに登録する。Geofence半径指定つき(単位:メートル)
on setLocationToEnterForReminderID(aLatitude, aLongitude, aTitle, theID, aRangeByMeter)
  – Get event store
  
set eventStore to current application’s EKEventStore’s alloc()’s initWithAccessToEntityTypes:(current application’s EKEntityMaskReminder)
  
– Get the reminder
  
set theReminder to eventStore’s calendarItemWithIdentifier:theID
  
–Create the geofence alarm
  
set enterAlarm to current application’s EKAlarm’s alarmWithRelativeOffset:0
  
enterAlarm’s setProximity:(current application’s EKAlarmProximityEnter)
  
set structLoc to current application’s EKStructuredLocation’s locationWithTitle:aTitle
  
set aLoc to current application’s CLLocation’s alloc()’s initWithLatitude:aLatitude longitude:aLongitude
  
structLoc’s setGeoLocation:aLoc
  
  
– Set radius by meters
  
structLoc’s setRadius:aRangeByMeter
  
enterAlarm’s setStructuredLocation:structLoc
  
  
theReminder’s addAlarm:enterAlarm
  
set aRes to eventStore’s saveReminder:theReminder commit:true |error|:(missing value)
  
  
return aRes as boolean
end setLocationToEnterForReminderID

–指定場所を出発した時に発動するGeofence Alarmを指定のリマインダーに登録する。Geofence半径指定つき(単位:メートル)
on setLocationToLeaveForReminderID(aLatitude, aLongitude, aTitle, theID, aRangeByMeter)
  – Get event store; 1 means reminders
  
set eventStore to current application’s EKEventStore’s alloc()’s initWithAccessToEntityTypes:(current application’s EKEntityMaskReminder)
  
– Get the reminder
  
set theReminder to eventStore’s calendarItemWithIdentifier:theID
  
–Create the geofence alarm
  
set leaveAlarm to current application’s EKAlarm’s alarmWithRelativeOffset:0
  
leaveAlarm’s setProximity:(current application’s EKAlarmProximityLeave)
  
set structLoc to current application’s EKStructuredLocation’s locationWithTitle:aTitle
  
set aLoc to current application’s CLLocation’s alloc()’s initWithLatitude:aLatitude longitude:aLongitude
  
structLoc’s setGeoLocation:aLoc
  
  
– Set radius by meters
  
structLoc’s setRadius:aRangeByMeter
  
leaveAlarm’s setStructuredLocation:structLoc
  
  
theReminder’s addAlarm:leaveAlarm
  
set aRes to eventStore’s saveReminder:theReminder commit:true |error|:(missing value)
  
  
return aRes as boolean
end setLocationToLeaveForReminderID

★Click Here to Open This Script 

2015/09/24 Remindersのイベントに場所(geofence alarm)を追加する

Reminders.app(日本語名:リマインダー)の指定イベントに場所(geofence alarm)を追加するAppleScriptです。

リマインダーのAppleScript用語辞書では「場所」についてはまったくサポート機能が用意されておらず(El CapitanのRemindersでもサポートなし)、全世界のScripterが肩を落としたものでした(Apple純正アプリのAppleScript用語辞書の残念さは本当に残念です。Finderのタグなど登場して相当の時間が経っているのに機能が追加されていません。タグについてはASOCで直接操作できるので、Appleの対応をもはや期待していない昨今)。

rem5.png
▲リマインダー(Reminders.app)のAppleScript用語辞書にはLocation関連の機能はない

そこで、出来のよくないリマインダーをアテにせず、Cocoaの機能を呼び出して、AppleScriptだけで「場所」の情報を追加するAppleScriptを書いてみました(Shane Stanleyの添削済み)。

この「場所」については、若干の説明が必要です。

geofence.png

緯度・経度でピンポイントの場所を指定できますが、ピンポイントすぎるのと誤差を許容するため「だいたいこのあたり」といった範囲の指定が必要になります。これを「Geofence」と呼ぶようです。

このピンポイントの場所から「半径XXXXメートル以内」のGeofenceに入った場合のアラーム(Enter Alarm)と、出た場合のアラーム(Leave Alarm)の2種類のうちどちらかを設定できるとのこと。

逆に、入るとか出るとかいうアクション指定のないものも作れますが、これがどういう挙動をするのかいまひとつ分かりません(Geofence内でずっと何かの通知が出る?)。

rem1.png
▲テスト用に作ったリマインダー項目

rem2.png
▲初期状態では何も「場所」関連の情報はついていません

rem3.png
▲本Script実行後の状態

rem4.png
▲内容を確認すると、意図したとおり「ゲームシティ板橋」のGeofenceが追加

img_2822_resized.png
▲ゲームシティ板橋店

AppleScript名:Remindersのイベントに場所(geofence alarm)を追加する
– Created 2014-12-08 by Shane Stanley
– Modified 2015-09-24 by Takaaki Naganoya
– Modified 2015-09-24 by Shane Stanley

–Reference:
–http://stackoverflow.com/questions/26903847/add-location-to-ekevent-ios-calendar
–http://timhibbard.com/blog/2013/01/03/how-to-create-remove-and-manage-geofence-reminders-in-ios-programmatically-with-xcode/

use AppleScript version “2.4″
use scripting additions
use framework “Foundation”
use framework “EventKit”

–Fetch an event to add geofence alarm
tell application “Reminders”
  tell account “iCloud”
    tell list “ホーム” –Use “Home” in English environment or your favorite one
      set theID to id of (last reminder whose completed is false)
      
–>  ”x-apple-reminder://868467FD-1F90-47EF-A9B1-A39334341D41″
    end tell
  end tell
end tell

set theID to text 20 thru -1 of theID
–> “868467FD-1F90-47EF-A9B1-A39334341D41″

setLocationToEnterForReminderID(35.745769, 139.675565, “ゲームシティ板橋”, theID, 200)

on setLocationToEnterForReminderID(aLatitude, aLongitude, aTitle, theID, aRangeByMeter)
  – Get event store; 1 means reminders
  
set eventStore to current application’s EKEventStore’s alloc()’s initWithAccessToEntityTypes:2 –Deprecated in OS X 10.9 ?
  
(* Enums: { EKEntityMaskEvent:1, EKEntityMaskReminder:2} *)
  
  
– Get the reminder
  
set theReminder to eventStore’s calendarItemWithIdentifier:theID
  
  
–Create the geofence alarm
  
set enterAlarm to current application’s EKAlarm’s alarmWithRelativeOffset:0 –ここがキモ
  
  
enterAlarm’s setProximity:1 – EKAlarmProximityEnter
  
(* Enums: { EKAlarmProximityNone:0, EKAlarmProximityEnter:1, EKAlarmProximityLeave:2 } *)
  
  
set structLoc to current application’s EKStructuredLocation’s locationWithTitle:aTitle
  
set aLoc to current application’s CLLocation’s alloc()’s initWithLatitude:aLatitude longitude:aLongitude
  
structLoc’s setGeoLocation:aLoc
  
  
– Set radius by meters
  
structLoc’s setRadius:aRangeByMeter –by meters
  
enterAlarm’s setStructuredLocation:structLoc
  
  
theReminder’s addAlarm:enterAlarm
  
set aRes to eventStore’s saveReminder:theReminder commit:true |error|:(missing value)
  
  
return aRes as boolean
end setLocationToEnterForReminderID

on setLocationToLeaveForReminderID(aLatitude, aLongitude, aTitle, theID, aRangeByMeter)
  – Get event store; 1 means reminders
  
set eventStore to current application’s EKEventStore’s alloc()’s initWithAccessToEntityTypes:2 –Deprecated in OS X 10.9 ?
  
(* Enums: { EKEntityMaskEvent:1, EKEntityMaskReminder:2} *)
  
  
– Get the reminder
  
set theReminder to eventStore’s calendarItemWithIdentifier:theID
  
  
–Create the geofence alarm
  
set enterAlarm to current application’s EKAlarm’s alarmWithRelativeOffset:0 –ここがキモ
  
  
enterAlarm’s setProximity:2 – EKAlarmProximityLeave
  
(* Enums: { EKAlarmProximityNone:0, EKAlarmProximityEnter:1, EKAlarmProximityLeave:2 } *)
  
  
set structLoc to current application’s EKStructuredLocation’s locationWithTitle:aTitle
  
set aLoc to current application’s CLLocation’s alloc()’s initWithLatitude:aLatitude longitude:aLongitude
  
structLoc’s setGeoLocation:aLoc
  
  
– Set radius by meters
  
structLoc’s setRadius:aRangeByMeter –by meters
  
enterAlarm’s setStructuredLocation:structLoc
  
  
theReminder’s addAlarm:enterAlarm
  
set aRes to eventStore’s saveReminder:theReminder commit:true |error|:(missing value)
  
  
return aRes as boolean
end setLocationToLeaveForReminderID

★Click Here to Open This Script 

2014/12/08 Remindersのイベントの期日を追加する

リマインダー(Reminders.app)の特定のreminderに対して、「期日」を追加するAppleScriptです。

Twitter上で、reminderに「場所」を追加できないだろうかという問いがあって・・・AppleScriptでは、リマインダー(Reminder.app)のAppleScript用語辞書には「場所」の予約語がないので、そのままでは手も足も出ません。ただ、ASOC経由でCocoaのEventKitにアクセスすれば、できそうな雰囲気がありました。

US AppleのAppleScript Users MLの過去ログをあさってみたら、きわめて近い内容のものがあったので、実際に試してみたら・・・OS X 10.10.1上ではエラーに。

ML上でShane Stanleyに聞いてみたら「10.10上では少々直す必要がある」とのことで、その場で教えてもらえました。ドキュメントを読めば分かる的な話ではあったのですが、Appleのオンラインドキュメントには定数の値が書かれていません(自分はここでスタックしていました)。

・・・どうやらShaneのいう「ドキュメント」とは「ヘッダーファイル」のことだったらしいことに後から気づきました。

# Script Editor上で編集中のAppleScriptのuse文をスキャンして、どのヘッダーファイルをオープンするかをユーザーに尋ねてオープンするAppleScriptを書いておく必要がありそう、、、

とりあえず、本Scriptでは「場所」ではなく「期限」を追加します。さらに調査を行えば、「場所」の追加もできそうですが・・・。

eventkit1.png

iCloud上の「ホーム」というリストにある(最新の)reminderに対して、「期限」を追加します。

eventkit2.png

AppleScript名:Remindersのイベントの期日を追加する
– Created 2014-03-08 by Shane Stanley
– Fixed 2014-12-08 by Shane Stanley
use AppleScript version “2.4″
use scripting additions
use framework “Foundation”
use framework “ASObjCExtras”
use framework “EventKit”

–とりあえず、1つのreminderのIDを取得
tell application “Reminders”
  tell account “iCloud”
    tell list “ホーム”
      set theID to id of (last reminder whose completed is false)
    end tell
  end tell
end tell

set theID to text 20 thru -1 of theID
–> “x-apple-reminder://B63F627B-A177-4CA7-9811-2AEB4875B407″
setDueDate_forReminderID_(((current date) + (2 * days)), theID)

on setDueDate:ASDate forReminderID:theID
  
  
– ASのdateをNSDateに変換
  
set theNSDate to my makeNSDateFrom:ASDate
  
  
– Cocoaのカレンダーを取得
  
set theCalendar to current application’s NSCalendar’s currentCalendar()
  
  
– Components of dateを取得
  
set theComponents to theCalendar’s componentsInTimeZone:(current application’s NSTimeZone’s localTimeZone()) fromDate:theNSDate
  
  
– get event store; 1 means reminders –> 2
  
set eventStore to current application’s EKEventStore’s alloc()’s initWithAccessToEntityTypes:2
  
  
– get the reminder
  
set theReminder to eventStore’s calendarItemWithIdentifier:theID
  
  
– 「期限」の日付を設定する
  
theReminder’s setDueDateComponents:theComponents
  
  
– 保存
  
eventStore’s saveReminder:theReminder commit:true |error|:(missing value)
  
end setDueDate:forReminderID:

on makeNSDateFrom:theASDate
  
  
set {theYear, theMonth, theDay, theSeconds} to theASDate’s {year, month, day, time}
  
  
if theYear < 0 then
    set theYear to -theYear
    
set theEra to 0
  else
    set theEra to 1
  end if
  
  
set theCalendar to current application’s NSCalendar’s currentCalendar()
  
set newDate to theCalendar’s dateWithEra:theEra |year|:theYear |month|:(theMonth as integer) ¬
    |day|:theDay hour:0 minute:0 |second|:theSeconds nanosecond:0
  
  
return newDate
  
end makeNSDateFrom:

★Click Here to Open This Script 

2013/12/19 Calendar.appのカレンダーを名称で抽出。Reminderのlistが見えてしまうものは除く

カレンダー(Calendar.app。旧称=iCal)上でカレンダーを名称で指定して取得するAppleScriptです。

カレンダー上では、自分自身のカレンダーだけでなく、あろうことかリマインダーのリストまで取得できてしまいます。これは、OS Xのバグというよりは、バカというべきか……カレンダー(Calendar.app)上では両者の識別方法はまったくありません。

さらに、両方とも「ホーム」といった同じ名前のカレンダー/リストがデフォルトで存在しており、カレンダー(Calendar.app)上では両者がまったく識別できません。

これは、Wordでオープン中の書類を問い合わせたら、ついでにPowerPointの書類まで返ってきて、さらにAppleScriptからはWord書類もPowerPoint書類も区別がつかない状態、といったら分りやすいでしょうか。関係ない他のアプリケーションのデータが見えてしまうなんて、バカもやすみやすみ言っていただきたいものです。

cal_rem.png

海外の仕事で調査中にこれに気付いて真っ青になりました。リマインダー側のリストの名称を手作業で替えてもらってなんとかなりましたが、1年に数回ある「クパティーノの某社に殺意を感じる一瞬」のうちの1回をカウントアップしてしまった次第。

AppleScriptから操作する分には、AppleScriptで取得できるデータの範囲内でしか処理できません。この、Appleのバグ的な仕様のためにひどい目にあわされています。バグレポートに書いたものの、Appleのエンジニアは文字が読めないか、自分たちで仕事をしていないためか、今日に至るも直っていません。

そのため、これを避けるための処理を書いてみました。動作原理は(苦労させられている割には)わりと簡単です。

まず、リマインダーにリストのIDをすべて問い合わせておいて、カレンダー(Calendar.app)で取得されるカレンダーのIDと付け合わせを行って、「カレンダー(Calendar.app)上で見えてしまう、リマインダーのリスト」を除外するという処理を行っています。

スクリプト名:Calendar.appのカレンダーを名称で抽出。Reminderのlistが見えてしまうものは除く
set aRes to getCalendarObjFromCal(“ホーム”) of me
–> {calendar id “8D0AC013-2AF0-4EA1-A563-AE59C811EB36″ of application “Calendar”}

–Calendar.appのカレンダーを名称で抽出。Reminderのlistが見えてしまうものは除く
on getCalendarObjFromCal(aName)
  
  
–先に、リマインダーのカレンダー(list)のIDを取得しておく
  
tell application “Reminders”
    set rList to id of every list
  end tell
  
  
–カレンダーを抽出するさいに、名称でしぼりこむほか、リマインダーのカレンダーを除外する
  
tell application “Calendar”
    set r2List to every calendar whose name is equal to aName
    
    
set r3List to {}
    
    
repeat with i in r2List
      set anID to uid of i
      
if anID is not in rList then
        set the end of r3List to (contents of i)
      end if
    end repeat
    
    
return r3List
  end tell
  
end getCalendarObjFromCal

▼新規書類に ▼カーソル位置に ▼ドキュメント末尾に