2代目日産リーフZE1の5年目車検を終えて

2017年10月に納車された2代目日産リーフZE1の5年目車検を済ませました。まだ数年は乗ろうと考えています。

以下では5年間リーフに乗って得られたデータを紹介します。

5年間の走行距離は7万7千kmでした。急速充電を821回、普通充電を46回しました。アパート住まいですので基本は急速充電、ホテルやショッピングモールで普通充電を使うこともある程度です。

EVの購入時に最も心配となる走行用電池の劣化についても、それなりのレベルに抑制されています。ZE1の発表当時に言われた「1年1万km走行しても10年で劣化10%以内」とはいかないもの、9割近い容量が残っています。80%充電から高速道路を走行して200kmくらいは走行できる感触です。体感レベルで新車の時より1割強は短くなっていますが、そもそも200kmをぶっ通しで走ることはないので不便になった感じはしません。

走行用電池の経年劣化と走行距離の推移

EVといえば電気で走る分だけガソリンよりランニングコストが低いと宣伝されます。私のケースではZESP2という大盤振る舞いのおかげで5年間の充電費用が13万4千円でした。毎月の走行距離とガソリン価格を記録してあるので、比較すると35万円ほど安上がりになった計算です。燃料費のほかに、消耗品類にお金がかからないという点も良いです。エンジンオイルはありませんし、回生ブレーキで消耗しないのでブレーキパッドは交換していません。

2代目日産リーフは5年経ってもまだまだ使い物になります。初代の電池劣化が激しかったのとは大違いです。とりあえず次の車検まで2年間は乗るつもりです。

Power AutomateでTeamsのチャネルのリンクからチャネルIDとチームIDを取得する

Power Automateでチャネルへの投稿を自動化したい場合、チャネルIDとチームIDが必要です。この二つのIDをユーザーが取得しやすい「チャネルのリンク」から取り出す方法を紹介します。

Teamsのチームの各チャネルには直接チャネルにアクセスできるURLがあります。このURLはTeamsのチャネル名の右にある三点ボタンからメニューを開き「チャネルへのリンクを取得」を選択することで取得できます。ここで得られるURLには、チャネルIDとチームIDが含まれています。したがって、「チャネルのリンク」を入力として、「チャネルID」文字列と「チームID」文字列を出力とするフローを組むことができます。

フローの全体像

Power Automateのフローは以下の通りです。各処理の中身は後述します。

各アクションの解説

チャネルのリンク
データ操作の作成アクションです。「チャネルへのリンクを取得」で取得したURLを与えています。このアクションの代わりに、トリガーで入力するようにしたり、Power Appsから渡すようにしておくと活用しやすいでしょう。

URIデコード
データ操作の作成アクションです。入力には、作成アクション「チャネルのリンク」の出力を用いて「decodeUriComponent(outputs('チャネルのリンク'))」のように指定します。
「チャネルへのリンクを取得」で取得したURLは本来URIで使用できない文字列をエンコードした形式になっています。これを本来の文字列に直す変換操作であるURIデコードを行います。

URIを配列に分ける
データ操作の作成アクションです。入力には、作成アクション「URIデコード」の出力を使って式を「split(outputs('URIデコード'),'/')」と指定します。
チャネルのリンクは、以下のような構造をしています。「/」で文字列を分割すれば、5番目がチームID、6番目のgroupID=以降がチャネルIDであることがわかります。なお、0番始まりで数えている点に注意してください。

https://teams.microsoft.com/l/channel/{チームID}/{チャネル名}?groupId={チャネルID}&tenantId={テナントID}

チャネルID
データ操作の作成アクションです。入力には、作成アクション「URIを配列に分ける」の出力を使って式を「outputs('URIを配列に分ける')[5]」と指定します。

テキストの位置の検索
テキスト関数のテキストの位置の検索アクションです。入力の「テキスト」には、作成アクション「URIを配列に分ける」の出力を使って式を「outputs('URIを配列に分ける')[6]」と指定します。また、入力の「検索テキスト」に「groupId=」と指定します。
「outputs(‘URIを配列に分ける’)[6]」で取得した文字列から「groupId=」の位置を割り出し、後段の操作でこの文字列よりも後ろの部分を抜き出します。

部分文字列
テキスト関数の部分文字列アクションです。入力の「テキスト」には、作成アクション「URIを配列に分ける」の出力を使って式を「outputs('URIを配列に分ける')[6]」と指定します。また、入力の「開始位置」に式「add(outputs(‘テキストの位置の検索’)?[‘body’],8)」と指定します。
「outputs(‘URIを配列に分ける’)[6]」で取得した文字列から「groupId=」の開始位置を割り出し、「groupId=」の文字数8を加えることで、「groupId=」の後ろの文字列を抽出します。

チームID
データ操作の作成アクションです。入力には、作成アクション「部分文字列」の出力を使って式を「split(outputs('部分文字列')?['body'],'&')[0]」と指定します。
「&」の後ろに続くtenantIdが不要なので、「&」で文字列を分割したうえで配列の先頭(0番目)の文字列を取り出します。

取得したチームIDとチャネルIDの使い方

Teamsの「チャットまたはチャネルでメッセージを投稿する」アクションを使う場合は、投稿先をチャネルとして上で、チームとチャネルに取得したIDを指定します。取得したIDを設定するには「カスタム値の入力」を選択します。

PowerAppsの「PowerAppsまたはFlowに応答する」アクションを使う場合は、出力追加でテキストを選択し、取得したIDの出力を追加します。

Power Automateの実行ユーザーから特定の個人へTeamsチャットのメッセージ送信

PowerAutomateのインスタントクラウドフローは、フローの所有者のほかに「実行のみユーザー」という権限でほかのユーザーに共有できます。この機能を使って、フローを実行するユーザーAから別のユーザーBへTeamsチャットでメッセージを送信することを考えます。

Teamsコネクタのアクションを調べていくと、チャットまたはチャネルでメッセージを投稿するを使用すればチャットのメッセージを送れるような気がします。ところが、チームのチャネルに投稿する方法とグループチャットに投稿する方法はわかりやすいわりに、普通のチャットすなわち個人から個人へチャットする場合はどうしたら良いかぱっと見でわかりません。そこで、このページでは個人から個人へチャットを送る方法を2つ紹介します。

フローの例

フローの例を下図に示します。分岐の右側のフローが方法1、左側が方法2です。

方法1: グループチャットの宛先に1人だけ入れる

それアリ?という感じがしますが、これが最もそれらしい振る舞いをします。

まず、チャットの作成アクションで追加するメンバーに宛先となるメールアドレスを入力します。ここで、宛先のメールアドレスがひとつだけの場合、フロー実行者と宛先の2者間のチャットを指定する会話IDが返ります。グループチャットのタイトルを指定する欄がありますが、タイトルを何にしようと影響はなく、常に2者間の通常のチャットを指定する会話IDが返ります。

こうして得られた会話IDを使って、チャットまたはチャネルでメッセージを投稿するアクションを動作させます。投稿者をユーザーとすればフローの実行者がチャットを送信することになります。投稿先をグループチャット、グループチャットには先ほどの会話IDを入れます。メッセージの記入欄が表示されるので、そこに動的なコンテンツを含むメッセージ本文を記入します。リンクを張りたい場合などは、ボタンを押してHTMLを記述します。

チャットの作成アクションの宛先について注意が必要な点として、実行ユーザー自身を指定できません。もしチャットの宛先にフローの実行者が入る可能性がある場合は、後述する方法2を使用します。

チャットの作成アクションは、宛先が複数のときと今回試したような宛先が1つのときで挙動が違うのに注意してください。宛先が複数あるとき、すなわちグループチャットを作成する場合にはグループチャットの構成メンバーとタイトルが同一であろうとチャットの作成アクションが動作するごとに異なる会話IDが生成されます。

方法2: フローボットとのチャット

チャットまたはチャネルでメッセージを投稿するアクションで、投稿者を「フローボット」、投稿先を「フローボットとチャットをする」に設定します。
宛先の指定は、Recipientに受信者のメールアドレスを指定するだけで十分です。
メッセージの記入欄が表示されるので、そこに動的なコンテンツを含むメッセージ本文を記入します。リンクを張りたい場合などは、ボタンを押してHTMLを記述します。

PowerAppsカレンダー画面の拡張 データソースをOutlookからSharePointリストに変えて

PowerAppsではシナリオとして典型的な操作を行うための画面が用意されています。複雑な機能を実現するのにシナリオで用意された画面を改変することで自分の目的を達成することができます。この記事では、予定表の表示に使用する「カレンダー」シナリオを拡張する例を紹介します。

シナリオで用意された画面の構成を調べる

改変するには元がどうなっているかを理解する必要があります。「カレンダー」シナリオで用意された画面の構成を調べてみましょう。

PowerAppで新しい携帯電話レイアウトのキャンバスアプリを作成します。続いて、新しい画面を使いする操作から、シナリオの「カレンダー」を選択します。

操作に成功すると、次のような画面が表示されます。画面上部のドロップダウンはPowerAppsで表示する予定表を選択するのでしょうか。ドロップダウンの右のカレンダーアイコンはまだよくわかりません。画面の下部はまずドロップダウンから予定表を選択せよとの指示です。

アプリの動作を確認するため、画面右上の再生ボタンかAltキーを押しながら画面をクリックします。ドロップダウンから「日本の休日」を選択すると、画面が以下のように変わります。

この画面を見ると、「カレンダー」シナリオの画面構成がどうなっているか、だいたい見当がつくでしょう。画面上段は表示すべきカレンダーの選択、画面中段は日付の選択、画面下段は選択した日に入っている予定の一覧です。

また、ドロップダウンを操作したときに画面の大部分が表示されたことから、ドロップダウンに主要なデータ操作が仕込まれていると予想できます。そこで、ドロップダウンのOnSelectやOnChangeを見てみましょう。

ドロップダウンのOnSelectを見ると、何行にもわたるコードが記述されていました。各行を読んでいくと、Office365Outlookコネクタを利用して予定表に登録されている予定の一覧を読み出すGetEventsCalendarViewV2を実行している行があります。この行ではClearCollect関数を使って、読みだした予定の一覧をMyCalendarEventsという名前のコレクション(テーブル)に保持するようだとわかります。

予定表から読みだされるデータはどのような形式かを確認します。PowerAppsの編集画面からコレクションの中身を確認してみましょう。次のような横に長い画面が出てきます。

ここで大事なのは列名とその列にどのようなデータを入れることになっているかです。また、多くの列がありますがほとんどPowerAppsのアプリでは使用していないデータです。一通り列名とデータを確認すると、このあと使いそうなデータは次の4つくらいでした。

列名入っているデータ(予想)入っているデータの型(予想)
End予定の終了年月日時刻時刻
Location予定の場所テキスト
Start予定の開始年月日時刻時刻
Subject予定の題名テキスト

Outlookの予定表から読みだした各予定の情報は高々この程度です。これらがClearCollect関数でMyCalendarEventsという名前のテーブルに格納されているとわかったら、同じ形式のテーブルを用意してやれば、「カレンダー」シナリオの画面をそのまま使いまわせるでしょう。具体定期には、SharePointリストで上の表にまとめたような列を持つリストをOutlook予定表の代わりにデータソースにできるはずです。

SharePointリストに登録したアイテムを予定として読み出す

前のセクションで予定を入れるのに必要なSharePointリストの列は4つ(End, Start, Location, Subject)だろうと見積もりました。このような列を持つSharePointリストを作ってみます。

EndとStartは「日付と時刻」型の列とします。時間を含めるを「はい」にして列を作成します。LocationとSubjectは1行テキストとします。

試しにデータを入れてみました。ここでは「Outlook代替」という名前のリストにしています。

ここで作成したSharePointリストをPowerAppsで読み込んでみましょう。

まずSharePointリストをPowerAppsから参照できるようにします。「データ」>「データの追加」>「SharePoint」で作成した「Outlook代替」リストを指定します。

次にSharePointリストのデータをPowerAppsの「カレンダー」シナリオに合うように読み込みます。単にMyCalendarEventsという名前のテーブルに「Outlook代替」リストの中身を流し込めば十分です。下の例では「SharePoint読込」というボタンのOnSelectに以下のような関数を定義しています。

ClearCollect(MyCalendarEvents, Outlook代替);

「日本の休日」カレンダーには存在しない「hoge」なるイベントを予定表に表示できました。「Outlook代替」で仮に登録したデータが表示されています。

ついでに表示される項目を変更してみましょう。上の図で太字のhogeと表示されているのはSubject列の値です。この部分のTextにはThisItem.Subjectが定義されています。これをThisItem.TitleにすればTitle列の値が表示されます。

「カレンダー」シナリオでは、開始時刻Start、件名Subject、所要時間(終了時刻Endと開始時刻Startの差分を分単位で表示)、場所Locationを表示する構成になっています。これを置き換えたり、追加したり削除したりすることで、自分の用途に必要十分な予定表を作れるでしょう。

予定表に表示される予定の条件を変更する

「カレンダー」シナリオの初期条件では、選択した日付に開始される予定が一覧に表示されます。予定を一覧表示するギャラリーのItemsは次のように定義されています。Text関数はDateTimeFormat.ShortDateで年月日を返します。

SortByColumns(Filter(MyCalendarEvents, Text(Start, DateTimeFormat.ShortDate) = Text(_dateSelected, DateTimeFormat.ShortDate)), "Start")

それでは、複数日にまたがる予定はどうなるでしょうか?先に作成した「Outlook代替」リストには25日から27日またがる予定「ほげ」が入っています。しかし、この予定表では26日や27日に予定「ほげ」が表示されません。

関数を書き換えて、日をまたぐ予定も表示されるようにしましょう。SortByColumnsはただの並べ替えですので、まず注目すべきはFilter関数の中身です。

Filter(MyCalendarEvents, Text(Start, DateTimeFormat.ShortDate) = Text(_dateSelected, DateTimeFormat.ShortDate))

予定の開始日時Startと予定の終了日時Endを使って、選択した日(0時から24時)にかかる予定を表示するようにするにはどうしたらいいでしょうか。状況を整理するため、下図を見てください。表示したい予定の条件は何通りかありますが、表示したくない予定は「終了時刻が選択日の0時より前」と「開始時刻が選択日の24時以降」の2パターンであることがわかります。

「終了時刻が選択した日の0時よりも前の予定」と「開始時刻が選択した翌日0時以降の予定」はいずれも選択した日にかかる予定ではありません。したがって、これの否定Notを取ればいいわけです。選択した日の0時_dateSelectedを使って以下のように書けます。

日付の大小関係を比較するにはDateTimeValue関数で日付を数値に変換します。また、日付の計算にはDateAddまたはDateDiff関数を使います。日付の計算は特殊なので、使い方の例も見てみましょう。

Filter(MyCalendarEvents, 
    Not(
        Or(
            DateTimeValue (End) < DateTimeValue (_dateSelected),
            DateTimeValue (Start) >= DateTimeValue (DateAdd( _dateSelected, 1, Days))
        )
    )
)

否定Notで書くと一見してわかりにくいので、ド・モルガンの法則を使って書き換えておきます。「(AまたはB)の否定」は「(Aの否定)かつ(Bの否定)」に同値です。日本語で表現すれば、予定の終了日時が選択日の0時以降でありかつ予定の開始日時が選択日の24時よりも前ということです。日本語で表現しても、それっぽい感じがします。

Filter(MyCalendarEvents, 
        And(
            DateTimeValue (End) >= DateTimeValue (_dateSelected),
            DateTimeValue (Start) < DateTimeValue (DateAdd( _dateSelected, 1, Days))
        )
    )
)

これで当日にかかる予定で複数日にまたがるものを拾えるようになりました。

「カレンダー」シナリオでは、予定が入っている日付の下に小さい丸が表示されます。これの予定が入っている判定は前述の条件と同じく予定の開始日が日付と一致するです。前節で改変したように、複数日にまたがる予定がある場合に、予定がかかるすべての日に対して小さい丸を表示するようにしてみましょう。

小さい丸のVisibleプロパティは次のように指定されています。

/*Visible if calendar events are found on this day*/               
CountRows(Filter(MyCalendarEvents, DateValue(Text(Start)) = DateAdd(_firstDayInView,ThisItem.Value,Days))) > 0 
&& !Subcircle1.Visible 
&& Title2.Visible

予定の開始日時のDateValue(年月日)が、この日と同じ
かつSubcircle1(選択された日表示)がVisibleでない
かつTitle2 (日付) がVisibleである
という意味です。

1行目のCountRows関数に与えている当日の予定を抽出するFilter条件式を改変すれば目的を達成できそうです。

Power AppsのデータソースをSharePointリストだけでがんばる

追加出費なく使える範囲でPower Platformを使い倒すため、データソースをSharePointリストだけで頑張る方法を調べました。

Power Appsのフォームコントロールで「個人」型データを指定する

編集フォームでリストに書き込む

詳細な説明はリンク先を参照

フォームで個人型に対応する値をメールアドレスなどから構成する場合は、対応するカードのUpdateやDefaultを以下のように指定します。

{
 Claims: "i:0#.f|membership|" & User().Email,
 DisplayName: User().FullName
}

Patch関数でリストに書き込む

詳細な説明はリンク先を参照

Patch関数の更新で個人型のレコードを書き込む列名に対して、以下のような値を指定します。

Claimsにメールアドレスが正しく入ってれば、Displynameはじめほかの値は適切な値に置き換えてリストに保存される模様。

{
    Value:"",
    '@odata.type':"#Microsoft.Azure.Connectors.SharePoint.SPListExpandedUser",
    Claims:  "i:0#.f|membership|" & Lower(User().Email),
    Department: "",
    DisplayName: "",
    Email: "",
    JobTitle: "",
    Picture: ""        
  }

Power Appsで取得した画像をAutomateを経由せずにリストに格納する

詳細はリンク先を参照

画像をJSON形式で扱うことで、リストの複数行テキスト列に格納できます。この方法であれば、SharePoint内に画像ライブラリや画像の保存フォルダを用意する必要がありません。

画像を文字列で扱えるといっても、フォームコントロールを介してリストにデータを送ることはできません。代わりに、Patch関数またはCollect関数でSharePointリストにアイテムを追加します。

JSON形式で画像を扱うとき

JSON関数に画像を読み取るコントロールのphotoやimageプロパティを渡します。ここでは、グローバル変数ImageJsonにカメラコントロールCamera1で取得した画像を設定するコードを紹介します。

Set(
 ImageJson,
 JSON(Camera1.photo,IncludeBinaryData)
);

SharePointリストに投稿するとき

JSON形式の画像ImageJSONをSharePointリストの複数行テキスト列GAZOに格納します。Patch関数を使った場合は次のようになります。

Patch( リストの名前,
  Defaults( リストの名前 ),
 { Title: "タイトル列の値",
   GAZO: ImageJSON
  }
 )

JSON形式で格納された画像を表示するとき

複数行テキスト列の値を読むと、ダブルクォーテーションで囲まれた文字列が返ります。このダブルクォーテーションを除去する操作をします。画像コントロールなどのimageプロパティに以下のような設定を行います。
ここではリストの2番目のレコードのGAZO列の値を表示します。

Substitute( Index( リストの名前, 2).GAZO,"""","")

PowerAppsでオフィス内宅配ロッカーを作ろう

Power Appsを使って、オフィス内で荷物をやり取りする宅配ロッカーのような仕組みを作ります。

情報をやり取りする手紙がメールに置き換わっても、小包のような荷物の配送は依然として大量に存在します。郵便局が扱う手紙が減っても荷物の宅配が増加しているのはその一例です。オフィスの中で荷物を配達するのに、宛先の人のデスクに荷物を置いておくというやり方があります。一方で、最近ではフリーアドレスのようにオフィス内に決まった居場所を持たない働き方が増えています。決まった居場所・デスクのない方へ宛てた荷物を受け渡すための仕組みとして、宅配ロッカーのような仕組みを考えてみましょう。

問題の整理

オフィス内で荷物をやり取りするために必要な機能を宅配ロッカーを参考に考えてみましょう。

  • 荷物を一時的に保管できるスペース(ロッカー)がある。
  • 宛先の人物に、荷物が保管スペースへ納品されたことを知らせる。
  • 宛先の人物に、荷物を回収するように催促する。
  • 宛先の人物が荷物を回収したら、その保管スペースに新たな荷物を設置できるようになる。

他にも、世間の宅配ロッカーには宛先の人物以外が荷物を取り出せないといった機能があります。これは不特定多数の利用者がいる場合に必要ですが、小規模なオフィス内では省略しても問題ないと考えます。また、形状もロッカーである必要はなく、テーブルなどモノを置ける場所に区切りや仕切りを施せば十分です。

また、利用ケースとして、外部から届いた荷物を配送する場合だけでなく、同じオフィスで働く従業員間で荷物をやり取りするケースも想定します。

設計

ソフトウェア

  • 各保管スペース(ロッカー)の情報を管理するSharePointのリスト
  • 各保管スペース(ロッカー)の情報を配達員または利用者が書き換えるためのPower Appsアプリ
  • 利用者に納品通知や回収の催促を送るためのPower Automateフロー

ハードウェア

  • 荷物を一時保管するためのスペース
    広めのデスクをテープ
  • 各保管スペース(ロッカー)に対応するPower Appsアプリを呼び出すためのURLを埋め込んだQRコード
  • 利用方法を案内する掲示物

業務のフロー

荷物の預け入れ

配達員は保管スペースに荷物を設置します。また、配達員は保管スペースに掲示されたQRコードをスマートフォンで読み込むことでPower Appsアプリを起動します。
配達員はアプリを通して、荷物の受取人を選択します。このとき配達員から受取人に対して伝言があれば、伝言を記入します。受取人と伝言を記入してアプリから送信すれば、配達員の手続きは完了です。
Power Appsアプリから送信されたデータはSharePointのリストに格納されます。SharePointのリストを監視しているPower Automateのクラウドフローが受取人に対して通知を送ります。通知には、荷物が届いた旨、どの保管スペースに荷物があるかの情報、配達員が誰かの情報、配達員からの伝言が記載されます。

荷物の受け取り

受取人は保管スペースで荷物を回収します。また、受取人は保管スペースに掲示されたQRコードをスマートフォンで読み込むことでPower Appsアプリを起動します。
受取人はアプリを通して、荷物を回収したことを送信します。
Power Appsアプリから送信されたデータはSharePointのリストに格納されます。SharePointのリストを監視しているPower Automateのクラウドフローが受取人に対して通知を送ります。通知には、荷物を回収したことを確認する旨が記載されます。

荷物回収の催促連絡

未回収の荷物がある場合、カレンダーベースのPower Automateで当該荷物の受取人に通知を送ります。特定の日数(1週間など)を超えて放置されている場合、配達員にも通知を送ります。荷物の放置が通知された場合、配達員は受取人の所属グループ同僚や上司に対して荷物の回収を催促します。


作り方

各保管スペース(ロッカー)の情報を管理するSharePointのリスト

荷物のやり取りに必要な情報を一元的に管理します。

どのロッカー(保管場所)を操作するかをID列で指定します。

列名列名(内部)列の型目的
タイトルTitle一行テキスト人の目で見てわかりやすい各保管スペース(ロッカー)の名前を示します。
SharePointのリストで自動生成されるタイトル列を使用します。
IDID数値各保管スペース(ロッカー)と紐づけます。
SharePointのリストで自動生成されるID列を使用します。
受取人Receiver個人荷物の宛先(受取人)をユーザーで指定します。
荷物ありhasItemはい/いいえ荷物が設置されているか否かを示します。荷物がある場合は「はい」、荷物がない場合は「いいえ」です。
配達人PostedBy個人荷物を各保管スペースに設置したユーザーを指定します。
荷主Sender一行テキスト組織外部から荷物を発送した荷主の社名や名前を示します。荷物を各保管スペースに設置したユーザーではありません。
伝言message一行テキスト配達員から受取人への伝言を示します。
更新日時Modified日付最後に情報が更新された日時を示します。
SharePointのリストで自動生成されるModified列を使用します。

SharePointのリストで作ったイメージが次の図です。


各保管スペース(ロッカー)の情報を配達員または利用者が書き換えるためのPower Appsアプリ

SharePointのリストに対して、PowerAppsの編集フォームコントロールによる編集操作を行います。荷物があるかないかをhasItem列で判別し、hasItem列が「はい」の場合は荷物の回収動作、hasItem列が「いいえ」の場合は荷物の預け入れ動作を行います。

荷物の預け入れ動作 hasItem列が「いいえ」の場合

預け入れ動作用のスクリーンを表示します。

荷物を設置しようとする対象のロッカー名およびIDを表示します。

入力欄として、宛先人のユーザー選択を必須とします。必須でない項目として、荷主の名前と宛先人への伝言を入力できるテキストボックスを用意します。
この入力欄に対して「登録」ボタンを用意し、SubmitFormで入力欄の情報をリストに登録します。また、リストのhasItem列を「はい」に変更します。

荷物の回収動作 hasItem列が「はい」の場合

荷物の回収動作用のスクリーンを表示します。

荷物を設置しようとする対象のロッカー名およびIDを表示します。また、その時点で設定されている荷物の宛先ユーザー名、荷主の名前、宛先人への伝言、更新日時、荷扱人を表示します。

入力欄として、「荷物回収」ボタンだけを表示します。このボタンが押されたとき、リストのhasItem列を「いいえ」に変更します。


利用者に納品通知や回収の催促を送るためのPower Automateフロー

リストの変更があったときに動作するフローと、定時で動作するフローの2つを作ります。前者は荷物の預け入れと回収に関する通知、後者は回収の催促に使用します。

荷物の預け入れと回収に関する通知

「自動化されたクラウドフロー」をSharePointの「アイテムが作成または変更されたとき」トリガーで実行します。

トリガーからは、アイテムが変更された後の最新の情報がフローに渡されます。
リストのhasItem列が「はい」のときは荷物が預けられたことを意味します。このとき受取人に対して荷物が届いたので回収するように連絡します。また、荷物を預けたユーザー(配達人)には入力を受け付けた旨を通知します。
リストのhasItem列が「いいえ」のときは荷物が回収されたことを意味します。このとき受取人には入力が受け付けられたことを通知します。また、配達人には荷物が回収されたことを通知します。

回収の催促

「スケジュール済みクラウドフロー」を1日ごとに実行します。

宅配ロッカーリストをSharePointコネクタ複数の項目の取得ですべてのロッカーのデータを取得します。取得したデータに対して、Apply to eachで個々のロッカーごとのレコードを取り出します。ここでhasItem列がはいであれば荷物があることになりますから、hasItem列の値を条件に代入します。条件で比較するときに注意が必要なのが、hasItemと比較する対象は式エディタでtrueと入力するという点です。ベタ書きで「はい」や「true」と書いても一致判定してくれません。
hasItemtrueのときだけ、Teamsのチャットまたはチャネルでメッセージを投稿するアクションを使って、受取人にメッセージを送ります。


アプリ作成の詳細

道具

各保管スペース(ロッカー)の情報を配達員または利用者が書き換えるためのPower Appsアプリの作成詳細

画面遷移

アプリが読み込む情報によってユーザーに求める動作が変わります。そこで、まずは画面遷移の構成を考えます。

PowerAppsのアプリ起動時に表示する画面は、ツリービュー>画面>AppのStartScreenで設定できます。If関数を使って、アプリが読み込むデータに基づいて適切な画面を表示できるように設定します。

荷物がすでにロッカーに存在する場合は、荷物の回収動作をします。荷物がない場合は、荷物の預け入れ動作をします。これらはSharePointのリスト宅配ロッカーにある情報を読み込めばhasItem列の値で判定できます。
SharePointのリスト宅配ロッカーのID列の値をロッカー番号として使いクエリパラメータLockerID=#の形で与える場合、表示すべき画面の分岐は次のようになります。hasItem列がはいTrueの場合は荷物の回収動作いいえFalseの場合は荷物の預け入れ動作という名前の画面からアプリがスタートします

If(LookUp(宅配ロッカー,ID=Param("LockerID")).hasItem,
   荷物の回収動作,
   荷物の預け入れ動作
)

ここでもう一つ考えたいのが、エラー処理です。SharePointのリスト宅配ロッカーのデータがないLockerIDを指定されたり、そもそもLockerIDの値が与えられていない場合、アプリは正しい処理をできません。そのような場合、早めにユーザーへ警告したほうがよいでしょう。そこで、IsBlankOrError関数を使って、SharePointのリスト宅配ロッカーのデータを見つける動作を正常に行えなかった場合に失敗画面へ遷移するようにしておきます。

If(IsBlankOrError(LookUp(宅配ロッカー,ID=Param("LockerID"))),
    失敗画面, 
    If(LookUp(宅配ロッカー,ID=Param("LockerID")).hasItem,
        荷物の回収動作,
        荷物の預け入れ動作
    )
)

これでアプリ起動時の画面遷移ができました。ほかに後で出てくるデータ送信が成功したときのための成功画面も作っておくとよいでしょう。

荷物を預け入れる動作画面をつくる

まずは、荷物がない場合つまりhasItemが「いいえ」のときを考えます。これは新しく荷物を設置して、受取人に連絡する業務フローです。

「編集フォーム」コントロールを用意します。これをForm1と呼びます。Form1のデータは、SharePointのリストで作成した「宅配ロッカー」リストを使用します。Form1のプロパティ>データはDataSourceが「宅配ロッカー」、DefaultModeがFormMode.Editです。ここで編集対象のロッカー番号を指定して、指定したアイテムだけを編集したいと考えます。テキスト入力コントロールを挿入し、名称を「ロッカー番号入力」とします。ロッカー番号入力にロッカー番号を記入して、ロッカー番号がIDと一致するアイテムを編集対象とするには、Itemを以下のように書きます。
LookUp(宅配ロッカー,ID=Value(ロッカー番号入力.Text))
LookUp関数はデータテーブルから1個のレコードを抽出する関数です。第1引数はデータソース(リスト)の名前、第2引数は条件式です。ロッカー番号入力.Textでロッカー番号入力コントロールに記入された文字列を読み取ります。IDは数値なのでロッカー番号入力.Textを数値変換するようにValue関数にかけます。

ユーザーに手作業をさせると、必ずどこかで間違いが生じます。いまロッカー番号を手入力するような形で作りましたが、アプリ起動時のURLから自動的にロッカー番号を設定できるようにしましょう。画面遷移のパートで書いたように、Param関数を使ってURLから文字列を取り出すことができます。FormのItemを次のように記述すれば、Param関数から直接ロッカー番号LockerIDを読み出すことができます。

LookUp(宅配ロッカー,ID=Value(Param("LockerID")))

続いて、フォームで編集する項目を変えてみましょう。
Form1のプロパティ>フィールド>フィールドの編集で、編集対象とする項目を編集できるようにましょう。ここでは、Title, Receiver, hasItem, Sender, message, PostedByを編集対象に加えます。

まず、hasItemはユーザーが値を入れるのではなく状態に応じて「はい/いいえ」が適切に変化するようにします。いまは荷物がないとこりに荷物を設置する場合を考えているので、操作をしたあたはhasItemが「はい」になるようにします。
ツリービューからhasItem_DataCard1のプロパティを開き、詳細設定Defaulttrueにします。trueは「はい」を意味します。逆にfalseは「いいえ」です。また、ユーザーが操作できないないように非表示します。同じく詳細設定Visiblefalseに設定します。

次に、受取人Receiverを工夫します。
表示がReceiverだと英語がわからない人に通じないので、受取人と表示されるようにします。ReceiverのDataCardでDisplayNameを”受取人”にしておけば、ほかの部分も連動して変わります。

Receiverはリストに「個人」型を設定したので、PowerAppsのForm1でも対応するUser型のデータを扱います。いまReceiver_DataCard1の下にDataCardValue2がある状態を考えると、DataCardValue2には選択されているユーザーのDisplayNameが表示されます。したがって、DataCardValue2で選択されているユーザーUserはDataCardValue2.Selectedで参照できます。たとえばDataCardValue2.Selected.Emailとすれば、選択されているユーザーのメールアドレスを取得できます。これを応用して、選択されているユーザーの姓だけを取り出すことができます。
Office365ユーザー.UserProfileV2(DataCardValue2.Selected.Email).surname
DataCardKey2詳細設定Textを以下のようにすることで、「受取人: 佐藤様」のような表示にできます。
"受取人: "&Office365ユーザー.UserProfileV2(DataCardValue2.Selected.Email).surname&"様"
ちなみに、このように表示項目に関数を仕込む場合は、関数がエラーを吐く場合に備えてIsBlankOrError関数でエラー処理を組み込んでおくと良いです。

PostedByもアプリを開いたユーザーが指定すればいいだけなので、同様にVisibleをfalseにしておきます。次にDefaultをアクセスしているユーザーにしたいのですが、これが一筋縄に行きません。Power AppsからSharePointのリストに送信するデータの形式をSharePointのリストの個人型に合わせるため、フォームで送信する値を工夫する必要があります。解決方法はリンク先を参照してください。工夫が必要になる原因はPowerAppsとSharePointのリストでデータ形式が不一致を起こすためです。なんのためのローコードか。

アプリを開いているユーザーを指定する最小の設定では、PostedBy_DataCard1 > DataCardValueのDefaultSelectedItemsを以下のように記述します。

{
 Claims: "i:0#.f|membership|" & Lower(User().Email),
 DisplayName: User().FullName
}

最後に送信ボタンを設置しましょう。
「ボタン」コントロールを挿入します。詳細設定のデータ>Textには「荷物を登録」のような文言を記入します。このボタンで重要なのはアクション>OnSelectにフォーム送信アクションを設定することです。
SubmitForm(Form1)
Form1の内容がSharePointのリストに送信されます。

フォームの送信に成功したことがユーザーわかるようにするため、「正常に完了しました」とだけ表示する画面に遷移させます。フォーム送信時に画面を遷移させるには、Form1のプロパティ詳細設定のOnSuccessに以下のように記述します。ここで遷移先のスクリーン名を成功画面としています。
Navigate(成功画面)
一方で、OnFailureはfalseのままでよいです。入力必須の項目が未入力の場合に警告を表示するような動作はFormの方で勝手にやってくれるので。

成功画面は予め1枚のスクリーンとして作っておきます。PowerAppsの編集画面左上にある「新しい画面」から「成功」を選ぶとそれらしい画面が生成されます。

ここまででフォームの編集ができるようになりました。
次に考えたいのが、前のデータ残っている場合です。

ここで想定しているSharePointのリストのデータ設計では、ロッカー番号に1対1でレコードがあります。これのレコードを書き換え続けます。したがって、荷物を受け取り済みにしても、前の荷物の情報が残ります。こうしておくと、前の荷物に関するトラブルが生じたときに直前回の情報がすぐに手に入るのでトラブル解決に便利です。
一方で、新しく荷物を預け入れたいのに、編集フォームで前の荷物の情報が出てきてしまうと困ります。誤った情報の登録にもつながります。そこで、SharePointのリストから読み取った情報の一部を消してPower Apps上で表示します。

Receiver_DataCard1の詳細設定>データ>DefaultBlank関数を入れることで、入力フォームの値が入っていない状態にできます。入力フォームがBlankだとSubmitformが動作しないので、意図しない受取人を設定するミスを予防できます。

受取人はBlank関数を使えばいいのですが、荷主Sender, 伝言messageはこの方法が使えません。Sender, messageは入力必須でない項目なので、値がBlank()でもSubmitformできます。値がBlankのままSharePointのリストに送信された項目は、前の値が更新されずに残ります。受取人Receiverは入力必須なのでSubmitformが動かずこのようにはなりません。そこで、Sender, messageは明示的に空の文字列を与えてやる必要があります。Blank()の代わりに“”と書きます。

荷物を回収する動作画面をつくる

次に、荷物がない場合つまりhasItemが「はい」のときを考えます。荷物を回収する動作画面では、荷物の受取人をユーザーとして想定します。また、想定する動作は荷物を回収するのみです。

荷物を預け入れる動作の画面とほとんど同じように作れます。こういうときはツリービュー>荷物の預け入れ動作の三点ボタンから「画面の複製」を行うと楽です。各アイテムの名前の末尾に_1が付与されて画面ごとに名前が異なるように調整されることに注意して下さい。

ツリービューからhasItem_DataCard1_1のプロパティを開き、詳細設定Defaultfalseにします。falseは「いいえ」を意味します。

この画面でユーザーに期待するのは荷物の回収だけです。そこで、既存のデータを書き換えられないようにしておきます。
編集フォームコントロール自体に、動作を編集・新規投稿・ビュー(閲覧のみ)に設定できます。しかし、編集フォームコントロール全体の設定で動作モードをビューに設定してしまうと、SubmitFormでSharePointのリストを書き換える動作も封じられます。そこで、画面に表示されている各入力欄の設定を表示のみに書き換えます。
ツリービューを見るとForm配下の末端にDataCardValueという名前の付いた入力系コントロールがあります。これが入力欄です。これの詳細設定の一番下の方にDisplayModeというプロパティがあります。これをDisplayMode.Viewに設定すると、動作時に表示のみになります。


配布の準備

作成したアプリを業務で使えるようにしましょう。

「共有」から公開を行うと、Web リンクに次のようなURLが表示されます。これが作成したアプリへアクセスするためのURLです。
https://apps.powerapps.com/play/example?tenantId=example

今回作成したアプリは、URLからロッカー番号を指定できるようにしました。前述のURLの末尾に&でLockerID=#をつなぎます。#にはロッカー番号を入れます。

例:ロッカー番号51のとき
https://apps.powerapps.com/play/example?tenantId=example&LockerID=51

このURLをQRコードのして印刷し、ロッカーの現場に表示しておけば、利用者はスマホでQRコードを読み込むだけでアプリにアクセスできます。URLからQRコードを作るにはqrqrなどを使います。


本アプリの作成を通じて業務の自動化を体験するワークショップを開催する場合の進め方例

1回目
小包、郵便をオフィス内で分配する業務フローを書き出してみる。
業務フローの中で困っていること、面倒なこと、間違いが起こりやすいことを探す。
消費者として使用した宅配ロッカーの経験を基に、宅配ロッカーの業務フローを書き出してみる。
これらを踏まえて、最終的なワークフローを書く。

2回目
SharePointのリストにデータを格納する方法を考える。
まずExcelにデータを入れて、手作業ですべての操作をすることを考える。
どんな列にどんなデータが入ればいいかを考える。
業務フローをカバーできるExcelができたら、これを基にSharePointのリストを作成する。

3回目
SharePointのリストに対して、PowerAppsからデータを操作できるようにする。
PowerAppsの勉強の導入になるので時間をかけて、リード役が画面を見せながら説明する。

4回目
SharePointのリストに対して、Power Automateで連絡のフローを作成する。
「自動化されたクラウドフロー」を「SharePointのアイテムが作成または変更されたとき」トリガーで実行する。

5回目
サービス展開に必要な資料を作成する。
ユーザーへの説明書はもちろん施策の狙いをまとめた資料を作成する。
ユーザーへの説明書はできるだけ具体的でわかりやすくする。ユーザーの支持を得るため。
施策の狙いをまとめた資料は、現場の切なる問題の提示とともにDXのような今時でいい感じの雰囲気をちりばめる。このような活動に肯定的な感触を持ってもらい、今後も自由に活動しやすくするため。

まったくの初心者にローコード開発を体験してもらうワークショップ Power Automate & Power Apps

プログラミングをしたこともなければ、Excelの関数もほとんど使ったことがないという方にPower AutomateとPower Appsを使ったローコード開発を体験してもらうワークショップの構成案を紹介します。

まず簡単な自動化を体験します。徐々に条件を複雑にして、実用的なアプリを作れるようになります。

Power Automateでメールを送ってみよう

メールを送る動作を自動化します。普段行う作業が容易に自動化できることを経験します。

Power Automateで決まった時間にメールを送ってみよう

メールを送る動作を予定したタイミングで実行します。ひとたびフローをくみ上げれば実行タイミングを自由に設定できることを経験します。

Power Automateで複数の条件に合うときだけメールを送ってみよう

条件分岐を学びます。関数を書くよりも、フローの中で視覚的に学んだ方がわかりやすいでしょう。「13日の金曜日」のように複数の条件に合致するときだけ動作するようにフローを書き換えます。

Power AppsからPower Automateを呼び出してみよう

Power Appsでユーザーインターフェースの作り方を学びます。すでにPower Automateで複数種類のトリガーによってフローを実行できると学んでいるので、新しいトリガーとしてPower Appsから呼び出すトリガーを導入します。フロー自体はすでに作ったことのあるメールの送信が良いでしょう。

Excelで関数の動作を経験しよう

Power Appsで関数を使うため、ひとまずExcelに移ります。Excelでは+演算子(数値の和)、&演算子(文字結合)、IF関数(簡単な関数)、VLOOKUP関数(引数が多く複雑な関数)を使って、関数が動作する経験をつかみます。

Power AppsのParam関数でアプリ外から値を制御しよう

関数を使った処理を学びます。プログラミング経験がない方に便利かつ意外性のある関数として、URLのクエリパラメータを解析するParam関数を紹介します。Param関数によってクエリパラメータがアプリ内に反映されることを経験します。

新型リーフ (2017) のバッテリー劣化 – 納車から4年半

新型リーフ (2017) のバッテリー劣化 – 新車48か月点検(車検)を終えての続編です。2017年10月の納車から4年半が経過しました。

バッテリーの劣化はゆっくりと進行しているようですが、体感できるような違いにはなっていません。いまのペースだと5年目の車検の時点で走行距離が8万kmになりそうです。車検時点でも、おそらくセグ欠けまで行かないでしょう。バッテリー劣化で悪評を広めた初代リーフとは大違いです。

LeafSpyで取得したSOHの経時データ。2017年10月納車、走行距離は1,000-2,000 km/月、気候は関東山間部。

最近はガソリン価格が高騰しているので、EVの低燃費の恩恵を受けています。毎月の走行距離とガソリン価格、プリウス並みの燃費(23 km/L)から計算すると、この4年半で30万円程度の燃料費削減になりました。これは充電費用定額というZESP2が安すぎることが大きな要因ですので、ZESP3だったらここまで利得は大きくないでしょう。

Power Appsを使い始めるにあたって参考になるページ

Power Appsを使い始めるにあたって参考になるページをまとめる。

公式

Microsoft公式の情報。辞書的なものであり、まずはこれを見る。

総論的

作例

Tips, リファレンス的

ノーコード・ローコード開発の考え方

東名高速道路鮎沢PA下りに急速充電器設置

東名高速の鮎沢パーキングエリア下り側にも急速充電器が設置されます。

これまで鮎沢PAは上り線だけ急速充電器が設置されていました。2022年2月現在まだ公式の発表は見つかりませんが、鮎沢PA下りには目隠しされた急速充電器の看板があり、充電器の設置工事も終わっているようでした。

充電器の前には充電マスが2台分、充電待機マスが1台分あります。

設置された充電器はABBのTerra 184です。充電ケーブルが2本、最大出力180 kWであり、2台同時に90 kWでの充電を行えるようです。ただし、最近同型とみられる急速充電器が56kWに出力を制限して運用されていることから、鮎沢PA下りも56kWで運用が始まる可能性が高いでしょう。

私の乗っている40kWhのリーフでは50kWまでしか対応していませんが、リーフe+なら70kW程度、アリアなら90kWに対応しているため、恩恵が大きいでしょう。


2022/3/21追記

東名本線上にあるEV QUICKの青看板から目隠しシールがはがされていたのでPAに入ったところ、使えるようになっていました。高速充電ナビや日産のアプリ、カーナビにも充電スポットとして表示されています。充電器の出力は各口56kWという表示でした。

ただし、使用状況は正確に共有されていないようです。リーフのカーナビ上はグレーアウトしており、利用状況を入手できない状態です。私が鮎沢で充電をはじめて、高速充電ナビでは使用中の表示にならず、2台空きのままでした。

リーフの車内では充電入力44kWでした。リーフへはだいたい充電器の出量の1割減の電力で充電されるので、充電器側は50kWで出力していたのだと推測しました。