Stackoverflowで見つけました。
最近のChromeで<input type"time">
を使うと右端に時計アイコンが出ます。これを消したい。
input[type="time"]::-webkit-calendar-picker-indicator { background: none; }
こうすると消えるみたい。大感謝…
Stackoverflowで見つけました。
最近のChromeで<input type"time">
を使うと右端に時計アイコンが出ます。これを消したい。
input[type="time"]::-webkit-calendar-picker-indicator { background: none; }
こうすると消えるみたい。大感謝…
TagHelperを継承したクラスで、ProcessAsyncをoverrideしていろいろすると、出力をいろいろできる。
// ターゲットにするタグ名をここに付ける [HtmlTargetElement("my:Example")] public class ExampleTagHelper : TagHelper { // ViewContextオブジェクトを格納してほしい時に書く。 // これがあると、TagHelperの内側からHttpContextとかにアクセスし放題になる [ViewContext] // 属性をBindしたくないときに付ける [HtmlAttributeNotBound] public ViewContext ViewContext { get; set; } // プロパティにバインドしたい属性を書く [HtmlAttributeName("hello")] public string Hello { get; set; } = ""; // これをoverrideしていろいろする public override Task ProcessAsync( // Bindした属性のリストとかはこっち TagHelperContext context, // Bindしてない属性のリストとか、出力するタグの設定とかはこっち TagHelperOutput output) { // 子要素のHTML-string はこれで読める var content = (await output.GetChildContentAsync()).GetContent(); // タグを my:Example から div に変える output.TagName = "div"; // 自己終了タグとして使いたいTagHelperなのに出力されるHTMLが自己終了だと都合が悪い時などにTagModeを変える // output.TagMode = TagMode.StartTagAndEndTag; // 属性を好きに付け替えたりできる output.Attributes.SetAttribute("style", "color: red"); // 中身も好き勝手編集できる output.PreElement.SetHtmlContent("[1]"); output.PreContent.SetHtmlContent("[2]"); output.Content.SetHtmlContent($"[3] {Hello}"); output.PostContent.SetHtmlContent("[4]"); output.PostElement.SetHtmlContent("[5]"); return Task.CompletedTask; } }
このTagHelperをRazorからこんな感じで呼ぶ
<my:Example hello="world" />
そうすると、こう出力される。
[1] <div> [2] [3] world [4] </div> [5]
便利。
Windows 10 2004 May 2020 Updateにするとシステムのバージョン情報に"Windows Feature Experience Pack"というのが表示されるようになりました。らしい。この環境はInsider Fast Ringなので若干バージョンが違うけれども、2004でも同じことができます。
Feature on Demand版のドキュメントを見てみると
This Feature on Demand package includes features critical to Windows functionality. Do not remove this package.
と、いうことらしい…中身が気になったので探してみます。
おそらくこれはAppX形式だろう。ということで、PowerShellでパッケージを探してたのがこれ。
PS> Get-AppxPackage | Where-Object { $_.Version -eq "120.13701.0.0" } Name : MicrosoftWindows.Client.CBS Publisher : CN=Microsoft Windows, O=Microsoft Corporation, L=Redmond, S=Washington, C=US Architecture : X64 ResourceId : Version : 120.13701.0.0 PackageFullName : MicrosoftWindows.Client.CBS_120.13701.0.0_x64__cw5n1h2txyewy InstallLocation : C:\Windows\SystemApps\MicrosoftWindows.Client.CBS_cw5n1h2txyewy IsFramework : False PackageFamilyName : MicrosoftWindows.Client.CBS_cw5n1h2txyewy PublisherId : cw5n1h2txyewy IsResourcePackage : False IsBundle : False IsDevelopmentMode : False NonRemovable : True Dependencies : {Microsoft.VCLibs.140.00_14.0.27810.0_x64__8wekyb3d8bbwe} IsPartiallyStaged : False SignatureKind : System Status : Ok
このパッケージは C:\Windows\SystemApps
に入ってるらしいので、管理者権限のコマンドラインから様子を見てみることにします。
PS> dir ディレクトリ: C:\Windows\SystemApps\MicrosoftWindows.Client.CBS_cw5n1h2txyewy Mode LastWriteTime Length Name ---- ------------- ------ ---- d----- 2020/06/14 7:20 AppxMetadata d----- 2020/06/14 7:20 Assets d----- 2020/06/14 7:20 InputApp d----- 2020/06/14 7:19 pris d----- 2020/06/14 7:20 ScreenClipping d----- 2020/06/14 7:20 WindowsInternal.ComposableShell.Experiences.SuggestionUIUndocked -a---- 2020/06/13 7:19 877169 AppxBlockMap.xml -a---- 2020/06/13 7:19 58385 AppxManifest.xml ..省略..
AppXらしいデータが入ってました。中身が気になるのでAppxManifest.xmlを眺めてみます。
> Get-Content .\AppxManifest.xml <?xml version="1.0" encoding="utf-8"?> <Package xmlns="http://schemas.microsoft.com/appx/manifest/foundation/windows10" xmlns:mp="http://schemas.microsoft.com/appx/2014/phone/manifest" xmlns:uap="http://schemas.microsoft.com/appx/manifest/uap/windows10" xmlns:uap3="http://schemas.microsoft.com/appx/manifest/uap/windows10/3" xmlns:uap4="http://schemas.microsoft.com/appx/manifest/uap/windows10/4" xmlns:uap6="http://schemas.microsoft.com/appx/manifest/uap/windows10/6" xmlns:uap10="http://schemas.microsoft.com/appx/manifest/uap/windows10/10" xmlns:wincap="http://schemas.microsoft.com/appx/manifest/foundation/windows10/windowscapabilities" IgnorableNamespaces="mp uap uap3 uap4 uap6 uap10 wincap"> <Identity Name="MicrosoftWindows.Client.CBS" Publisher="CN=Microsoft Windows, O=Microsoft Corporation, L=Redmond, S=Washington, C=US" Version="120.13701.0.0" ProcessorArchitecture="x64"/> <mp:PhoneIdentity PhoneProductId="3898fac3-1f84-4040-83ce-ef30739c0a64" PhonePublisherId="6145b212-a274-4517-8abe-1e015f21ff03"/> <Properties> <DisplayName>Windows Feature Experience Pack</DisplayName> <PublisherDisplayName>Microsoft Windows</PublisherDisplayName> <Logo>Assets\StoreLogo.png</Logo> </Properties> ..省略..
こういうのがだらだら続くのだけれども、めっちゃ長いのでピックアップして。
どうやらInputMethod的ななにかだそうです。
<Application Id="InputApp" Executable="TextInputHost.exe" EntryPoint="WindowsInternal.ComposableShell.Experiences.TextInputUndocked.InputApp.App"> ..省略.. <Extensions> <!-- VS's manifest validation won't accept an empty Extensions section but it is required for fragment merging, so add a dummy protocol entry --> <uap:Extension Category="windows.protocol"> <uap:Protocol Name="ms-inputapp" DesiredView="useMinimum"> <uap:DisplayName>Input App</uap:DisplayName> </uap:Protocol> </uap:Extension> <uap3:Extension Category="windows.appExtension"> <uap3:AppExtension Name="com.microsoft.windows.input.app" Id="InputApp" PublicFolder="Public" DisplayName="Input Experience"> </uap3:AppExtension> </uap3:Extension> </Extensions> </Application>
Windpws+Shift+Sで出てくるあれの実体がここに入っているそうです。
<Application Id="ScreenClipping" Executable="ScreenClippingHost.exe" EntryPoint="ScreenClippingHost.App"> ..省略.. <Extensions> <uap:Extension Category="windows.protocol"> <uap:Protocol Name="ms-screenclip"> <uap:DisplayName>ms-screenclip</uap:DisplayName> </uap:Protocol> </uap:Extension> <uap3:Extension Category="windows.appExtension"> <uap3:AppExtension Name="com.microsoft.windows.app.screenclip" Id="ScreenClippingApp" PublicFolder="Public" DisplayName="Screen Snipping Experience"/> </uap3:Extension> <uap3:Extension Category="windows.appExtensionHost"> <uap3:AppExtensionHost> <uap3:Name>com.microsoft.windows.protocoloverride</uap3:Name> </uap3:AppExtensionHost> </uap3:Extension> </Extensions> </Application>
今のところこれくらいしか中身はないのだけれども、EntryPointの名前とかパッケージの名前とかを見るとそのうちいろいろな実体がここに移されてくるんだろうなぁ…という感じでした。
4か月前に作ったこれを、毎日使っていたのですが、今日ふと電話置き場を眺めてみるとこんなことに。あらやだ。
電池って4か月ちょいでこんなことになってしまうんですね、こわいこわい。
さて、我が家のシーリングライト危機が訪れました。解決方法として考えられたのは
1は、まだまだあるので大丈夫といえば大丈夫なんですが4か月ごとに電話が死んでいくとなるとそれはさすがに困ります。 2は、あまり数は無く、あっても大抵が産業用なのでかなり高い。
そこで3つ目の解決方法がふと思いつきました。Raspberry Pi 3にAndroidをインストールして、そこで今までのシステムを動かす。 全開の図はこんな感じにアップデートされます。
かなりスマートになりました。adbを使っていた部分はもはやAndroidの内側になってしまったのでADBは不要になり、ローカルのコマンドを実行するだけになりました。
この作戦はRaspberry Pi 3 でAndroid が起動してかつBluetoothが比較的まともに動くことが条件でした。 今回は2個試して、ちゃんと使えたLineageOS 16.0を使います。
インストールはビルド済みイメージをダウンロードして、SDに書き込み、Raspberry Pi 3にカードを入れて起動すれば数分後にはAndroidの初回セットアップ画面が起動します。
セットアップはそこそこにすすめて、いつものAndroidと同じように開発者オプションを有効にして、ADBを有効にします。加えて、Rootアクセスも有効にしておきました。 これで基本的なセットアップはおしまい。
専用アプリがないとライトを操作できないので、専用アプリをどうにかしてインストールします。
Gappsをインストールする方法もあるのですが、このアプリはGappsなしでも動作するので ほかのAndroidデバイスからapkをコピーしました。コピー方法は割愛。
Bluetoothも正しそうに動いてBLEでライトと通信できる状態になりました。
この仕組みはインターネット上で動作しているNodeJSサーバが、LANの内側で動作しているNodeJSプロセスにSocket.IO通してGoogle Assistantからのコマンドを転送する仕組みになっていました。 大きく書き直すのは大変、そしてAndroidはなんだかんだLinuxなのでNodeJSが動くでしょう。ということでAndroid上でNodeJSを実行できるようにします。
Androidで使えるNodeJSのバイナリを探してみたものの、今風のバージョンは見つからず、ソースからビルドするのもいまいちうまくいかない。
そこで、Termuxをインストールし、その中で apt install nodejs
した結果インストールされたバイナリを使うことにしました。
TermuxはAndroid上で動作するLinux環境のようなものです。公式にAPKが配布されているのでダウンロードしてadb install
しました。
インストールが終わったら、Termuxを立ち上げてそこからはLinuxの世界なのでてきとうにNodeJSをインストールします。
$ apt update $ apt install nodejs
これでNodeJSがインストールできました。使う時は /data/data/com.termux/files/usr/bin/node
を参照するといいです。
あとはこのNodeJS環境でこのプログラムを実行するだけで今まで通りシーリングがON/OFFできます。うれしいですね!
const socket = require('socket.io-client')('https://***.com'); const { exec } = require('child_process'); const util = require('util'); const X = [136, 248]; const Y = [189, 373, 555]; const on = `${X[0]} ${Y[0]}`; const off = `${X[1]} ${Y[2]}`; const dimm = `${X[0]} ${Y[2]}`; socket.on('connect', () => { console.log('connect'); }); socket.on('execute', data => { const input = data.inputs[0]; for(const execution of input.payload.commands[0].execution){ if(execution.command === 'action.devices.commands.OnOff'){ const arg = execution.params.on ? on : off; exec(`input touchscreen tap ${arg}`); } if(execution.command === 'action.devices.commands.BrightnessAbsolute'){ const arg = execution.params.brightness ? on : dimm; exec(`input touchscreen tap ${arg}`); } } console.log(util.inspect(data, true, null)); });
一つだけ問題があって、HDMIに何もつながってないと起動しません。そして起動後HDMIを抜くと、
05-01 00:07:25.610 188 267 I hwc-drm-connector: force mode to 1280x720@0Hz 05-01 00:07:25.613 188 267 I hwc-drm-two: Unplug event @1596363841 for connector 29 on display 0 05-01 00:07:25.634 3546 3546 I HWC2 : Destroying display 0 05-01 00:07:25.637 3546 3546 E HWComposer: isConnected failed for display 0: Invalid display 05-01 00:07:25.637 3546 3546 E HWComposer: getPresentFence failed for display 0: Invalid display 05-01 00:07:25.637 3546 3582 E HWComposer: onVsync Failed to find display 0 05-01 00:07:25.637 3546 3546 E HWComposer: isConnected failed for display 0: Invalid display 05-01 00:07:25.637 3546 3546 E HWComposer: getPresentFence failed for display 0: Invalid display 05-01 00:07:25.651 3616 3639 I DisplayManagerService: Display device removed: DisplayDeviceInfo{"内蔵スクリーン": uniqueId="local:0", 1280 x 720, modeId 3, defaultModeId 3, supportedModes [{id=1, width=1280, height=720, fps=50.0}, {id=2, width=1280, height=720, fps=59.9402}, {id=3, width=1280, height=720, fps=60.0}], colorMode 0, supportedColorModes [0], HdrCapabilities android.view.Display$HdrCapabilities@40f16308, density 160, 54.186 x 53.788 dpi, appVsyncOff 1000000, presDeadline 16666667, touch INTERNAL, rotation 0, type BUILT_IN, state ON, FLAG_DEFAULT_DISPLAY, FLAG_ROTATES_WITH_CONTENT, FLAG_SECURE, FLAG_SUPPORTS_PROTECTED_BUFFERS} 05-01 00:07:25.684 3616 3631 E AndroidRuntime: *** FATAL EXCEPTION IN SYSTEM PROCESS: ActivityManager 05-01 00:07:25.684 3616 3631 E AndroidRuntime: java.lang.IllegalArgumentException: Can't remove the primary display. 05-01 00:07:25.684 3616 3631 E AndroidRuntime: at com.android.server.am.ActivityStackSupervisor.handleDisplayRemoved(ActivityStackSupervisor.java:4337) 05-01 00:07:25.684 3616 3631 E AndroidRuntime: at com.android.server.am.ActivityStackSupervisor.access$300(ActivityStackSupervisor.java:193) 05-01 00:07:25.684 3616 3631 E AndroidRuntime: at com.android.server.am.ActivityStackSupervisor$ActivityStackSupervisorHandler.handleMessage(ActivityStackSupervisor.java:4733) 05-01 00:07:25.684 3616 3631 E AndroidRuntime: at android.os.Handler.dispatchMessage(Handler.java:106) 05-01 00:07:25.684 3616 3631 E AndroidRuntime: at android.os.Looper.loop(Looper.java:193) 05-01 00:07:25.684 3616 3631 E AndroidRuntime: at android.os.HandlerThread.run(HandlerThread.java:65) 05-01 00:07:25.684 3616 3631 E AndroidRuntime: at com.android.server.ServiceThread.run(ServiceThread.java:44) 05-01 00:07:25.685 3616 3703 I InputReader: Reconfiguring input devices. changes=0x00000004 05-01 00:07:25.694 3616 3631 I Process : Sending signal. PID: 3616 SIG: 9 05-01 00:07:26.130 200 200 I lowmemorykiller: lmkd data connection dropped 05-01 00:07:26.131 200 200 I lowmemorykiller: closing lmkd data connection 05-01 00:07:26.152 3549 3597 W AudioFlinger: power manager service died !!! 05-01 00:07:26.152 3788 3803 W Sensors : sensorservice died [0xaba4dc00] 05-01 00:07:26.155 149 149 I ServiceManager: service 'telecom' died 05-01 00:07:26.155 149 149 I ServiceManager: service 'contexthub' died (...省略) 05-01 00:07:26.166 149 149 I ServiceManager: service 'imms' died 05-01 00:07:26.166 149 149 I ServiceManager: service 'autofill' died 05-01 00:07:26.166 149 149 I ServiceManager: service 'profile' died
DisplayがなくなったのでDisplayManagerがDisplayを削除して…するとActivityManagerが対応したディスプレイを削除…しようとしたけどPrimaryなので削除できない!!といったあとシステムがダウンしていったのでした。悲しい。
その結果、部屋の真ん中にRaspberry Piが放置されることに…
専用アプリがなぜかiPadにしかなくて、せめてiPhoneには出してよ…とおもいつつ、いろいろあれこれした結果がこちらです。
FontAwesome ProのフォントファイルはさすがにGitに乗せられないので、FreeのOTFを入れて参照するフォントを変えるか、ProのDuotoneを入れるとたぶん動きます。
Uno Platform、実際に使った話をあまりきかない*1のでちょっくら真面目に使ってみるかってことで使ってみたのがこちらのソースコードになります。
ほぼ同じコードで動いていて、違うコードを書かないといけなかったのは
で、それ以外は同じコードでできた。まぁまぁいい感じだと思いました。
開発中だし仕方ないんだけど、怪しい箇所はちらほらあって
とか確認しました。
*1:Hello Worldしかみんなやらない問題
UDR(Untethered Dead Reckoning)というのがあります。GNSSが受信できない状態で自車位置を推定する技術のうち、車両信号を利用しないものをこう呼ぶそうです。
u-bloxというスイスのメーカーからリリースされているNEO-M8シリーズのうち、NEO-M8UというモジュールがこのUDRをサポートしています。
NEO-M8Uは、3軸ジャイロと3軸加速度センサを内蔵していてセンサを通して車両の挙動を推定してくれます。なんかしらの計算結果を位置情報に反映してNMEA形式で推定結果を出力してくれるのでとても賢くて使いやすいんじゃないかと思ってこれを使います。
さっそくNEO-M8Uを使えるようにしたいと思います。まず、M5StackのGPSモジュールを用意します。このGPSモジュールはNEO-M8Nというモジュールが使用されています。似たような名前だけどこっちはUDRできないやつ。なんでM5StackのGPSモジュールを用意したかというと
という理由です。先に上げた通り、M8UはNEOシリーズの全モジュールと完全な互換性があるそうです。つまり、剥がして貼りなおせば使えるはず。
改造に必要なGPSモジュールとM8UはDigi-keyで用意しました。3日くらいで届いたのでDigi-keyさまさま。
さて改造といっても、剥がして
貼るだけ
剥がしスキルが低すぎて、PPSのランドが剥がれてしまったのはそのうち適当に配線しよう…
散々M5Stackがどうこう言っていたのにGPS部分しか持っていないのでまずはUARTをPCへ接続してu-centerで様子を見ます。Navigation Expert 5を確認すると最初からADR*1/UDRが有効になってました。
次に自動車へ設置。センサーで自動車の挙動を推定するので車両にしっかり固定しろよな!ということなのでとりあえずテープで止めました。
このままGNSS信号が受信できている状態でしばらく走っていると、センサーのキャリブレーションが終わってUDRが使えるようになるはず…とドキュメントに書いていました。 3D+DGNSSの状態で15分くらい車を走らせていると、キャリブレーションが完了したようです。
あとはこのままGNSS信号が受信できない場所へ行けばUDRの動作が確認できるはず!!!ということで近場のトンネルへ出かけてみたところ、UDRの動作が確認できました。
出口付近は推定結果がちょっと怪しめ。距離が長くなると誤差が累積してしまうので仕方ないといえば仕方ない。固定場所とかの条件にもよるのかもしれない。
ちなみに、UDR中のNMEAメッセージを見ると
$GNGLL,3438.68839,N,13525.04534,E,181743.00,A,E*7D
ちゃんとModeがEになっているのでちゃんと測位情報がDRで計算されてます。