28
43

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 5 years have passed since last update.

Amazon Echo で TVリモコン操作への道 (RPizero & AWSIoT & Lambda & AlexaSkill) 前編

Last updated at Posted at 2017-11-22

#1.全体構成

作成動機

我が家はなぜかTVリモコンがすぐなくなる。
なくなる、とは言っても、本当になくなるわけではなく、ソファーの下に潜り込んでいたり、椅子の下に落ちてたり、
なぜかTVの直下に置いてあったり。
家族が誰も所定の置き場、みたいなものを意識しないで使いっぱなしの為、こうなる。

その為、TVを付ける、消すの、ふとした時にすぐリモコンが見つからないと、イライラする。
朝急いでいる時なんか、イライラ倍増。

そこで、TVのON/OFF、もっと言うとCH操作をAmazonEcho経由で行えれば、万事解決なのでは?
と思い立ったのが直接の動機。

あとは、もともとAmazonEchoが家にあった、RaspberryPiZeroを買った、などの契機も重なったので。

構成メモ、ログを取ってたら、思いの外長くなりそうなので、3部作くらいになりそう。。


構成案

  • 去年(2016年)のRe:InventにてもらったAmazon Echo x1
  • RaspberryPi Zero W + 赤外線LED
  • AWS IoT
  • AWS Lambda
  • AlexaSkill

alext-rpi.jpg

ピタゴラスイッチなみw
ただ、要素毎の事例はたくさんあるので、1つ1つ組み合わせていくだけ、のはず。

参考URL
[AWS IOT]
(http://docs.aws.amazon.com/ja_jp/iot/latest/developerguide/iot-sdk-setup.html)
Qiita記事: AWS-IoT ShadowからLEDのオン・オフを行う(AWS IoT Device SDK for Pythonを用いて)
CloudWatch Events + LambdaでAWS IoTデバイスシャドウを制御する
Raspberry pi 3 で部屋の赤外線受信できる機器をコントロール!
Raspbian Stretchで LIRC機能を使った学習リモコン、赤外線リモコンを動かす方法
AWS IoT Device Shadowを試すためのpython mock

#2. IoTデバイス(RaspberryPi)準備

兎にも角にも、まずは直接TVリモコンを操作するIoT端末(RaspberryPi Zero)を作成。
Raspberry で赤外線リモコン、は様々な事例がQiitaにもあるので、基本はそちらを参考に。
本編ではポイントのみで。

##2.1 物理配線

準備

  • 5mm赤外線LED OSI5LA5113A  OSI5LA5113A x1
  • 抵抗(30Ω)くらい x1
  • 赤外線リモコン受信モジュールOSRB38C9AA OSRB38C9AA x1
  • RaspberryPi Zero W 1

配線図

tvrasp.png

LIRCの出力にGPIO17/入力GPIO18を使用した例。
あくまで参考まで、、

##2.2 リモコン操作電波受信

今回の目的の各の技術ではあるが、主題ではないので、要点のみの記載に留めてます。

Raspbian Stretchで LIRC機能を使った学習リモコン、赤外線リモコンを動かす方法
こちら参考に適宜環境で対応

###2.2.1 lircインストール

pi@raspberrypi:~ $ sudo apt-get update
pi@raspberrypi:~ $ sudo apt-get install lirc

###2.2.2 コンフィグ設定

一般的にここでハマることが多いようです。
lircのバージョン、raspbianのバージョンによって設定ファイルが違うためです。
環境にあった設定をしましょう。

(2017/11時点)
RaspberryPi Zero W
Kernel : Linux raspberrypi 4.9.59+ #104
lirc : 0.9.4c-9

この環境の場合は、以下の設定でOKでした。

/boot/config.txt
# Uncomment this to enable the lirc-rpi module
dtoverlay=lirc-rpi:gpio_out_pin=17,gpio_in_pin=18,gpio_in_pull=up
/etc/lirc/lirc_options.conf
pi@raspberrypi:~ $ diff /etc/lirc/lirc_options.conf.dist /etc/lirc/lirc_options.conf
11,12c11,14
< driver          = devinput
< device          = auto
---
> #driver          = devinput
> #device          = auto
> driver          = default
> device          = /dev/lirc0

ここで一度リブート。

###2.2.3 TVリモコン学習

起動後、一度lircdを停止。
手動で受信状態にし、その結果をteeでファイル保存させて、モジュールに向かって覚えさせるボタンをおす。
以下はtv電源ボタン例

raspberryPi
pi@raspberrypi:~ $ sudo /etc/init.d/lircd stop
[....] Stopping lircd (via systemctl): lircd.serviceWarning: Stopping lircd.service, but it can still be activated by:
  lircd.socket
. ok 
pi@raspberrypi:~/mytv_lirc $ mode2 -d /dev/lirc0 | tee tv_on
Trying device: /dev/lirc0
Using device: /dev/lirc0
Using driver default on device /dev/lirc0
space 7519518
pulse 8931
space 4483
pulse 667
space 451
pulse 560
space 562
pulse 610
space 510
pulse 609
space 509
~略

正常に受信出来ていれば、上記の様にspace/pulseが出力される。
これを覚えさせたいボタンの分、繰り返し実施。

保存結果ファイルから、以下パースプログラム(parse_pulse.rb)を使って出力数字を取り出す。

parse_pulse.rb
lines =  File.open(ARGV[0]).readlines
lines_without_first = lines.slice(1..-1)
puts lines_without_first.map{|line|line.split(" ")[1]}.join(" ")
pi@raspberrypi:~/mytv_lirc $ ruby parse_pulse.rb tv_on
7519518 8931 4483 667 451 560 562 610 510 609 509 608 517 560 565 603 1631 609 508 560 1684 610 1627 550 1684 600 1631 554 1685 549 1681 554 566 598 1640 552 576 587 1640 561 554 603 1641 593 1637 644 471 603 524 599 518 602 1640 597 523 596 1639 596 519 602 521 
~略

ここで上記の結果をlircd.conf.d/ 以下に任意のconf名で保存する。
lircd.conf.d/配下の不要なファイルは消しておく。(2重入力=dupicateエラーを避けるため)

ちなみに、以下注意点

  • コードの羅列は、80文字ほどで適宜改行を入れること(必須)。
  • パース後の出力結果の一番初めの桁数の多い数字(7519518など)はコピペしない。(桁の多い数字は、lengthエラーがでる)
/etc/lirc/lircd.conf.d/tv.conf
begin remote

  name  room
  flags RAW_CODES
  eps            30
  aeps          100

  gap          200000
  toggle_bit_mask 0x0

      begin raw_codes
    name tv_on
8931 4483 667 451 560 562 610 510 609 509 608 517 560 565 603 1631 609 508 560 
1684 610 1627 550 1684 600 1631 554 1685 549 1681 554 566 598 1640 552 576 587 
1640 561 554 603 1641 593 1637 644 471 603 524 599 518 602 1640 597 523 596 1639 
596 519 602 5211598 637 472 651 474 645 1601 639 528 591 474 645 489 634 1597 
637 480 651 1583 639 1599 635 490 633 1595 638 1617 618 1594 641 39645 9004 
2169 634
      end raw_codes
end remote

上記の要領で、必要そうな操作を全て登録しておく。
正常に登録されていると、以下の通りに登録信号が並ぶ。

pi@raspberrypi:~ $ sudo /etc/init.d/lircd start
[ ok ] Starting lircd (via systemctl): lircd.service.
pi@raspberrypi:~/mytv_lirc $ irsend LIST "" ""  room

room

0000000000000001 tv_on
0000000000000002 tv_off
0000000000000003 volup
0000000000000004 voldown
0000000000000005 ch1
0000000000000006 ch2
0000000000000007 ch4
0000000000000008 ch6
0000000000000009 ch7
000000000000000a ch8
000000000000000b ch9
000000000000000c ch10

lircを起動して赤外線の送信テスト。

raspberrypi

pi@raspberrypi:~ $ irsend SEND_ONCE room tv_on
pi@raspberrypi:~ $ irsend SEND_ONCE room tv_off
pi@raspberrypi:~ $ irsend SEND_ONCE room ch1
pi@raspberrypi:~ $ irsend SEND_ONCE room ch2
pi@raspberrypi:~ $ irsend SEND_ONCE room ch4
pi@raspberrypi:~ $ irsend SEND_ONCE room ch6
pi@raspberrypi:~ $ irsend SEND_ONCE room ch10
pi@raspberrypi:~ $ irsend SEND_ONCE room volup
pi@raspberrypi:~ $ irsend SEND_ONCE room voldown

これでうまく動けば、デバイス準備は完了!

以下は、RasPiZeroをフリスクケースに納めて、赤外線送信部分だけを、TVの前に向けた状態。
最終的にはテレビの裏に貼り付ける予定だけど、今は暫定でこのまま。

IMG_3288.JPG

#3. AWS IoT -> RaspberryPi

ピタゴラの1歩目。
作成した端末を「AWS IoT」のシャドウ機能を利用して、TV操作可能なようにする。

##3.1 AWS IoT登録

これはAWS IoTのデバイス登録ウィザードからほぼデフォルトのままサクサク進めてOK。
リージョンはもちろん東京を選択。

「AWS IoTに接続する」を選択

スクリーンショット 2017-11-11 15.55.27.png

「デバイスの設定」「今すぐ始める」を選択

スクリーンショット 2017-11-11 15.55.41.png

今回はRasbian + Pythonスクリプトを利用するので、
「Linux」 & 「Python」を選択

スクリーンショット 2017-11-11 15.56.14.png

名前は「myHomePi」としておく。(ちょっと美味そうなひびきだな。。)

スクリーンショット 2017-11-11 15.56.34.png

一応ポリシーなど含め、内容確認して問題なければ、接続キットをダウンロードする。

スクリーンショット 2017-11-11 15.56.47.png スクリーンショット 2017-11-11 15.57.05.png スクリーンショット 2017-11-11 15.57.37.png

無事登録完了。

スクリーンショット 2017-11-11 15.58.15.png

 
##3.2 登録デバイスのRestAPIエンドポイントを記録

後述のスクリプトに仕込むための、RestAPIエンドポイント情報を確認しておく。
デバイス管理画面から、「操作」を選択し、HTTPSの欄にあるホスト名をメモしておく。

スクリーンショット 2017-11-11 17.35.31.png

##3.3 デバイス側の連携準備

AWS SDKをインストール & 作業ディレクトリの作成

RaspberryPi
pi@raspberrypi:~ $  sudo pip install AWSIoTPythonSDK
Collecting AWSIoTPythonSDK
  Downloading AWSIoTPythonSDK-1.2.0.tar.gz (67kB)
    100% |████████████████████████████████| 71kB 528kB/s 
Building wheels for collected packages: AWSIoTPythonSDK
  Running setup.py bdist_wheel for AWSIoTPythonSDK ... done
  Stored in directory: /root/.cache/pip/wheels/c9/b1/9c/0ae77289f3e4049944a807981269309f83e906addfb0afe5c9
Successfully built AWSIoTPythonSDK
Installing collected packages: AWSIoTPythonSDK
Successfully installed AWSIoTPythonSDK-1.2.0

pi@raspberrypi:~ $ mkdir python_aws_iot
pi@raspberrypi:~ $ cd python_aws_iot/

3.1でダウンロードした接続キット(zipファイル)をRaspberryPiに送る。

MacBook端末
toguma-MacBook:~ toguma$ scp ~/Downloads/connect_device_package.zip pi@192.168.1.42:~/python_aws_iot
pi@192.168.1.42's password: 
connect_device_package.zip                                                                                                                    100% 3591   310.5KB/s   00:00  

解凍して、スクリプトを準備する。
この接続キット内にある、証明書(.cert.pem)とキー(private.key)はスクリプト登録に必要のため、certs ディレクトリに下記名前で保存しておく。

キー配置場所
./certs/

  • certs.pem (もともとのmyHomePi.cert.pem)
  • public.pem (もともとのmyHomePi.public.key)
  • private.pem (もともとのmyHomePi.private.key)
RaspberryPi
pi@raspberrypi:~/python_aws_iot $ unzip connect_device_package.zip 
Archive:  connect_device_package.zip
  inflating: myHomePi.private.key    
  inflating: myHomePi.public.key     
  inflating: myHomePi.cert.pem       
  inflating: start.sh                
pi@raspberrypi:~/python_aws_iot $ ls
connect_device_package.zip  myHomePi.cert.pem  myHomePi.private.key  myHomePi.public.key  start.sh
pi@raspberrypi:~/python_aws_iot $ mkdir certs/
pi@raspberrypi:~/python_aws_iot $ mv myHomePi.cert.pem certs/cert.pem 
pi@raspberrypi:~/python_aws_iot $ mv myHomePi.public.pem certs/public.pem 
pi@raspberrypi:~/python_aws_iot $ mv myHomePi.private.pem certs/private.pem 

次に、root証明書をダウンロードし、
certs/ ディレクトリ配下に 「root.pem」 という名前で保存する。

RaspberryPi
pi@raspberrypi:~/python_aws_iot $ curl https://www.symantec.com/content/en/us/enterprise/verisign/roots/VeriSign-Class%2ication-Authority-G5.pem > ./certs/root.pem
  % Total    % Received % Xferd  Average Speed   Time    Time     Time  Current
                                 Dload  Upload   Total   Spent    Left  Speed
100  1758  100  1758    0     0   1723      0  0:00:01  0:00:01 --:--:--  1725

##3.4 AWS IoTとの連携プログラムの作成、配置

IoTと連携用のPythonスクリプトを準備

接続情報のコンフィグレーションファイル setup.jsonを作成。
"ENDPOINT"
"DEVICE"-"NAME"
を、3.1で作成した「モノ」=Thingの内容に合わせて修正。

設定情報 setup.json

setup.json
{
   "AWSIoT": {
        "ENDPOINT":"xxxxxxxxxxx.iot.ap-northeast-1.amazonaws.com",
        "CERT_PATH":"./certs/",
        "KEYS":["cert.pem", "public.pem", "root.pem"]
   },
   "DEVICE":{
        "NAME":"myHomePi"
   }
}

連携スクリプト本体 Tv.py

Tv.py
import os
import sys
import json
import time
from AWSIoTPythonSDK.MQTTLib import AWSIoTMQTTShadowClient

CONFIG_FILE = './setup.json'

shadow         = None
shadow_hundler = None

##########################################################
# include setup.json
###########################################################
def readConfig():
    print 'start readConfig func'

    try:
        # read config file
        f = open(CONFIG_FILE, "r")
        jsonData = json.load(f)
        f.close()

        list = {}
        list["ENDPOINT"]    = jsonData["AWSIoT"]["ENDPOINT"]
        list["CERT_PATH"]   = jsonData["AWSIoT"]["CERT_PATH"]
        list["DEVICE_NAME"] = jsonData["DEVICE"]["NAME"]

        return list

    except Exception as e:
        print 'Config load error'
        print e.message
        sys.exit()

##########################################################
# Shadow update
##########################################################
def updateThing(report):
    try: 
        report_json = '{"state":{"reported":'+ report + '}}'
        print "send currnet status to cloud-shadow"
        print report_json
        shadow_hundler.shadowUpdate(report_json, None, 5)

        return

    except Exception as e:
        print e.message
        sys.exit()

##########################################################
# shadowRegisterDeltaCallback
#
##########################################################
def getDelta(payload, responseStatus, token):
    try:
        print '======get Delta======'
        dict_delta = json.loads(payload)
        print(dict_delta)

        for x in dict_delta["state"]:
            print("/usr/bin/irsend SEND_ONCE room " + x)
            cli = "/usr/bin/irsend SEND_ONCE room " + x
            os.system(cli)    

        report = json.dumps(dict_delta["state"])
        updateThing(report)

        return

    except Exception as e:
        print "Error on Delta function"
        print e.message
        raise

################################################
# Shadow session 
################################################
def initShadow(Config):
    ##--need device cert / private / rootCA--
    # rootCA: get from symantec
    ROOT_KEY    = Config['CERT_PATH'] + 'root.pem'
    CERT_KEY    = Config['CERT_PATH'] + 'cert.pem'
    PRIVATE_KEY = Config['CERT_PATH'] + 'private.pem'

    try:
        # init shadow connect procedure
        global shadow
        shadow = AWSIoTMQTTShadowClient(Config["DEVICE_NAME"])
        shadow.configureEndpoint(Config["ENDPOINT"], 8883)    # Setting URL-ENDPOINT & Port
        shadow.configureCredentials(ROOT_KEY, PRIVATE_KEY, CERT_KEY ) # Cert file setting
        shadow.configureConnectDisconnectTimeout(10)# CONNACK wait time (sec)
        shadow.configureMQTTOperationTimeout(5)     # QoS1 publish (sec)
        print 'start connct shadow'
        shadow.connect()
        print 'shadow connect'

        return

    except Exception as e:
        print 'Error on Init Shadow'
        raise

####
if __name__ == '__main__':
    Config = readConfig()

    try:
        initShadow(Config)
        print 'satrt subscribe shadow'
        shadow_hundler = shadow.createShadowHandlerWithName(Config['DEVICE_NAME'], True)
        shadow_hundler.shadowRegisterDeltaCallback(getDelta)

        while True:
            pass

    except KeyboardInterrupt:
         print 'Keyboard Interrupt'
         sys.exit()

    except Exception as e:
        print e.message
        sys.exit()

Tv.pyの大まかな処理内容

  • 指定のIoTデバイスと接続
  • IoT シャドウからdesiredreportedの差分値であるdeltaが発生を検知
  • deltaを検知後、delta内容を取得し、内容に応じた処理を実施

  登録するstate名をそのままリモコン登録した名前に(例えば tv_on)すると、
  desiredされたstate=Key名をそのままリモコン操作コマンドに割り当てることが可能。

リモコン処理抜粋
        for x in dict_delta["state"]:
            print("/usr/bin/irsend SEND_ONCE room " + x)
            cli = "/usr/bin/irsend SEND_ONCE room " + x
            os.system(cli)    
  • コマンド実行後、desired内容をreportedに登録(差分を埋める)

##3.5 IoT シャドウの登録

AWS IoTの登録したモノの、シャドウを編集

スクリーンショット 2017-11-18 16.11.58.png
シャドウ内容
{
  "desired": {
    "tv_on": 0,
    "tv_off": 0,
    "volup": 0,
    "voldown": 0,
    "ch1": 0,
    "ch2": 0,
    "ch4": 0,
    "ch5": 0,
    "ch6": 0,
    "ch7": 0,
    "ch8": 0,
    "ch9": 0,
    "ch10": 0
  },
  "reported": {
    "tv_on": 0,
    "tv_off": 0,
    "volup": 0,
    "voldown": 0,
    "ch1": 0,
    "ch2": 0,
    "ch4": 0,
    "ch5": 0,
    "ch6": 0,
    "ch7": 0,
    "ch8": 0,
    "ch9": 0,
    "ch10": 0
  }
}

ちなみに、初期設定で、aws_xxxx というstateが入っており、上記上書きしても消えません。
その場合は、消したいstateの desired / reported それぞれに「null」 を記載して保存すると、消えます。
これ公式ドキュメントに乗っているんですが、なかなか気がつかなかった。。
初期有用なTipsですw

##3.6 AWS IoT受信プログラム実行

これで実行準備が整ったので、受信プログラムを起動してみる。
AWS IoT側の設定と違っている箇所があるとエラー(config errorなど)になるので、その場合は再度よく確認しておくこと。

RaspberryPi
pi@raspberrypi:~/python_aws_iot $ python Tv.py 
start readConfig func
start connct shadow
shadow connect
satrt subscribe shadow

##3.7 AwS IoTのシャドウから直接操作

AWS IoTのコンソールから該当デバイスのシャドウの値のdesiredで、操作したいボタンの値を0->1に修正して編集、保存。
(ここで、reportedは修正しないこと。
受信プログラムはdesiredとreportedの差分を検知して動作するので、この部分が一緒だと差分が発生せずに動きません。)

「tv_on」の場合、以下のログを出力しつつ、TVが付くことを確認

RaspberryPi
pi@raspberrypi:~/python_aws_iot $ python Tv.py 
start readConfig func
start connct shadow
shadow connect
satrt subscribe shadow
======get Delta======
{u'timestamp': 1510989882, u'state': {u'tv_on': 1}, u'version': 116, u'metadata': {u'tv_on': {u'timestamp': 1510989882}}}
/usr/bin/irsend SEND_ONCE room tv_on
send currnet status to cloud-shadow
{"state":{"reported":{"tv_on": 1}}}

これでAWS IoTのシャドウからデバイス「myHomePi」(RaspberryPi)をTV操作する環境ができた。

#4 Lambda -> AWS IoT -> RaspberryPi

ピタゴラスイッチ2歩目。

今度はAWS IoTをLambdaから操作可能なようにする。
ここでは、Lambda経由でIoTを動かすための仮のテスト関数。
Alexa用には別途作成予定。
また仮ってことで、この関数だけサンプルと同じ、Nodejsで。
本番のAlexa連携用はPython利用予定。

##4.1 Lambda関数の作成

スクリーンショット 2017-11-11 17.31.56.png

関数名 : myHomePi-Tv-ctrl
言語 : Node.js 6.10
IAMPolycy : LambdaデフォルトのIAM権限+AWSIoTDataAccess
mem : 128NB
Timeout : 3s

スクリーンショット 2017-11-18 17.50.13.png

プログラム内容
エンドポイントの箇所を環境に合わせて修正。

プログラム概要

  • デバイスの状態(シャドウ)をAWSIoTから取得
  • eventから入力された操作タイプのstateのdesierdだけ、変更する。
    • (この時、値が0の場合に1を、1の場合に0を指定することで、スイッチのトグルが実装される。)
  • 変更した値をシャドウにupdateする。

と、やっていることは単純。

index.handler
console.log('Loading function');
var aws = require('aws-sdk');
 
var endpoint  = 'xxxxxxxxxxxxx.iot.ap-northeast-1.amazonaws.com';
var thingName = 'myHomePi';
 
exports.handler = function(event, context) {
    // AWS IoT Data APIに接続
    var iotdata = new aws.IotData( { endpoint: endpoint } );
    // デバイスシャドウを取得
    var params = { thingName: thingName };
    iotdata.getThingShadow(params, function (err, data) {
        if (!err) {
            // シャドウドキュメントから現在の設定を取得
            var payload = JSON.parse(data.payload);
            var conditionDetail = {};
            var ctrl_type = event.ctrl_type;
            var current_value = payload["state"]["desired"][ctrl_type]
            
            // 指定ctrl_typeをトグルし、シャドウドキュメントに合わせたオブジェクトを生成
            if(current_value == '0') {
                conditionDetail[ctrl_type] = Number(1);
            } else {
                conditionDetail[ctrl_type] = Number(0);
            }
            var desiredState = {
                state: {
                    desired: conditionDetail
                }
            };
             
            // デバイスシャドウを書き込む
            var params = {
              thingName: thingName,
              payload: JSON.stringify(desiredState)
            };
            iotdata.updateThingShadow(params, function (err, data) {
                if (!err) {
                    context.succeed();
                } else {
                    context.fail(err);      
                }
            });
        } else {
            context.fail(err);      
        }
    });
};

##4.2 Lambda実行テスト

テスト用jsonを以下の様に用意

test.json
{
  "ctrl_type": "tv_off"
}

テスト実行結果が、「成功」となり、以下の様なログが出ればOK。
テストjsonのctrl_typeを変更して、想定通りにTV操作が可能なことを確認する。

START RequestId: 6f59c7bf-cc3f-11e7-986a-299ff7ed3d52 Version: $LATEST
2017-11-18T09:04:08.550Z	6f59c7bf-cc3f-11e7-986a-299ff7ed3d52	Write state :{"state":{"desired":{"tv_on":0}}}
2017-11-18T09:04:08.667Z	6f59c7bf-cc3f-11e7-986a-299ff7ed3d52	thing Data Update Done.
END RequestId: 6f59c7bf-cc3f-11e7-986a-299ff7ed3d52
REPORT RequestId: 6f59c7bf-cc3f-11e7-986a-299ff7ed3d52	Duration: 515.47 ms	Billed Duration: 600 ms 	Memory Size: 128 MB	Max Memory Used: 34 MB

これで、Alexa Skillに登録するまでの下準備がようやく完了。
長くなったので、一旦ここまで。

次回後半編で、実際にAlexaとの連携を実行していく。
またタイミング的に日本語版が発売した時期なので、日本語対応化予定。

続きを書きました。
Amazon Echo で TVリモコン操作への道 (RPizero & AWSIoT & Lambda & AlexaSkill) 後編

28
43
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
28
43

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?