Skip to main content

Voice control Sony Bravia Television through Alexa

This is my second useful thing done through Alexa after simple implementation of switching on/off light. This is not just applicable to Sony Bravia TVs but any device which can be controlled through HTTP/JSON request or via any other protocol.

Hardware prerequisites for making whole thing work are as follows:
1. Sony Bravia Android TV or other devices which can accept input through HTTP or different protocol.
2. Raspberry Pi to keep running program/service.
3. Alexa device.

Software prerequisites:
1. Alexa Skill: https://developer.amazon.com/edw/home.html#/skills
2. Lambda: https://console.aws.amazon.com/lambda/home?region=us-east-1#/functions
3. AWS IoT: https://console.aws.amazon.com/iot/home?region=us-east-1

How the whole process would work?

Alexa would accept voice commands and converts it to intend to make a request to Lambda function. Lambda function would use converted user-friendly commands to MQTT request on AWS IoT service which would be listened through MQTT client running on Raspberry Pi through our own program. Running program would accept JSON mapped command and make a specific request to the TV.

TV configuration and test

Generating Pre-Shared Key:
This acts like authentication to make HTTP call. Setting can found under Settings > Network > Home Network > IP control > IP Control to enable it and Settings > Network > Home Network > IP control > Pre-shared Key for setting key access of IP access.
Sample request to control TV volume
Sample request to volume up

The above would make sense how it works, just that we need to compose these XMLs with different commands. The point is since it is a private IP address we need to make it through MQTT otherwise just hosting on static public IP or any domain provider would work directly. In that case no AWS IoT, MQTT required just creating an app with static public IP would take care of making requests through Lambda.

In this case, we would need MQTT, let's first complete the program.

Python program to interact

Let's begin with the actual program. At first, we need to get valid commands which can be executed on TV to control it. This request can give us adequate commands for TV commands.

Request for getting TV commands which can be used to control TV
URL:  http://<IP Address>/sony/system
Headers:
X-Auth-PSK: 198500000
SOAPAction: "urn:schemas-sony-com:service:IRCC:1#X_SendIRCC"
Body:
{
  "id": 3,
  "method": "getRemoteControllerInfo",
  "version": "1.0",
  "params": [
    "1.0"
  ]
}

The result of above is:
 The name is the command name and value is the actual value which has to places on TV.

The JSON could be saved in ircc_commands.json file.

Note: Coding credits goes to https://github.com/kranzrm/AlexaBraviaSkill. I just have altered codes based on my need. Both Python and Lambda codes are modified.

The main program would read from JSON file and execute the mapped command.

 #!/usr/bin/python  
 import paho.mqtt.client as mqtt  
 import ssl  
 import requests  
 import json  
 import os.path  
 import urllib2  
 ca_certs = "./root-CA.crt"  
 certfile = "./HomPi.cert.pem"  
 keyfile = "./HomPi.private.key"  
 aws_endpoint = "axxxxxxxxxxxxx.iot.us-east-1.amazonaws.com"  
 aws_port = 8883  
 def GetIp():  
   ip = '192.168.8.'  
   scanIp = 100  
   maxScan = 120  
   scan = True  
   while scan:  
     url = 'http://' + ip + str(scanIp) + '/sony/IRCC'  
     req = urllib2.Request(url)  
     try:  
       resp = urllib2.urlopen(url)  
     except urllib2.HTTPError as e:  
       if e.code != 403:  
         scanIp += 1  
         print "Next iteration:" + ip + str(scanIp)  
       else:  
         print 'Found: ' + ip + str(scanIp)  
         scan = False  
       if scanIp >= maxScan:  
         scan = False  
         print 'Unable to find resource.'  
         break  
     except urllib2.URLError, e:  
       scanIp += 1  
       print 'url error.'  
     else:  
       scan = False  
       print 'all okay'  
   return ip + str(scanIp)  
 url = "http://" + GetIp() + "/sony/IRCC"  
 # Get list of commands  
 command_list = {}  
 with open('./ircc_commands.json') as json_data:  
   d = json.load(json_data)  
   commandJson = sorted(d['result'][1])  
   for item in commandJson:  
     command_list[item['name']] = item['value']  
 def ircc_cmd(url,command):  
   cmds = command.split(",")  
   status = 0  
   for cmd in cmds:  
     if not command_list.has_key(cmd):  
       return 0  
     send_command(command_list[cmd])  
   return 1  
 def send_command(command):  
   headers = {  
     'content-type': 'text/xml',  
     'SOAPACTION': '"urn:schemas-sony-com:service:IRCC:1#X_SendIRCC"',  
     'X-Auth-PSK': '0000000', # Needs to be changed 
       }  
   body = """<?xml version="1.0" encoding="UTF-8"?>  
 <SOAP-ENV:Envelope xmlns:SOAP-ENV="http://schemas.xmlsoap.org/soap/envelope/" SOAP-ENV:encodingStyle="http://schemas.xmlsoap.org/soap/encoding/">  
   <SOAP-ENV:Body>  
    <m:X_SendIRCC xmlns:m="urn:schemas-sony-com:service:IRCC:1">  
      <IRCCCode xmlns:dt="urn:schemas-microsoft-com:datatypes" dt:dt="string">""" + command + """</IRCCCode>  
    </m:X_SendIRCC>  
   </SOAP-ENV:Body>  
 </SOAP-ENV:Envelope>"""  
   print('headers:' + str(headers))  
   print('body:' + body)  
   response = requests.post(url, data=body, headers=headers)  
   print(response.text)  
   return response.text  
 # The callback for when the client receives a CONNACK response from the server.  
 def on_connect(client, userdata, flags, rc):  
   print("Connected with result code " + str(rc))  
   # Subscribing in on_connect() means that if we lose the connection and  
   # reconnect then subscriptions will be renewed.  
   client.subscribe("bravia/command")  
 # The callback for when a PUBLISH message is received from the server.  
 def on_message(client, userdata, msg):  
   print(msg.topic + " " + str(msg.payload))  
   print("Command: " + msg.payload)  
   #print("Url: " + url)  
   err = ircc_cmd(url, msg.payload)  
   if err: print("Command not found")  
 client = mqtt.Client()  
 client.tls_set(ca_certs, certfile=certfile, keyfile=keyfile, cert_reqs=ssl.CERT_REQUIRED,  
   tls_version=ssl.PROTOCOL_SSLv23, ciphers=None)  
 client.on_connect = on_connect  
 client.on_message = on_message  
 client.connect(aws_endpoint, aws_port, 60)  
 # Blocking call that processes network traffic, dispatches callbacks and  
 # handles reconnecting.  
 # Other loop*() functions are available that give a threaded interface and a  
 # manual interface.  
 client.loop_forever()  

The HomPi (custom name) and certificate files changed through AWS IoT thing creation. These files need to be downloaded through AWS IoT portal.

There is IP scan code added with range limit since I had some limitation on my router but if you have a static IP address for local than it could be replaced with static one.

Once AWS IoT is configured this program can be executed.

The program would read incoming request and get the actual command value from ircc_commands.json file to execute.

Lambda function to read request from Alexa Skill

Again these codes are mainly taken from https://github.com/kranzrm/AlexaBraviaSkill and modified. In this, I have modified commands.js to support Tata Sky Channel Mapping and AlexaSkill.js to increase intend.

Index.js
 /**  
   Copyright 2014-2015 Amazon.com, Inc. or its affiliates. All Rights Reserved.  
   Licensed under the Apache License, Version 2.0 (the "License"). You may not use this file except in compliance with the License. A copy of the License is located at  
     http://aws.amazon.com/apache2.0/  
   or in the "license" file accompanying this file. This file is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License.  
 */  
 /**  
  * This sample shows how to create a Lambda function for handling Alexa Skill requests that:  
  *  
  * - Custom slot type: demonstrates using custom slot types to handle a finite set of known values  
  *  
  * Examples:  
  * One-shot model:  
  * User: "Alexa, ask Minecraft Helper how to make paper."  
  * Alexa: "(reads back recipe for paper)"  
  */  
 'use strict';  
 var AlexaSkill = require('./AlexaSkill'),  
   recipes = require('./commands');  
 var AWS = require('aws-sdk');  
 var APP_ID = 'amzn1.ask.skill.xxxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx'; //replace with 'amzn1.echo-sdk-ams.app.[your-unique-value-here]';  
 var TVControl = function () {  
   AlexaSkill.call(this, APP_ID);  
 };  
 // Extend AlexaSkill  
 TVControl.prototype = Object.create(AlexaSkill.prototype);  
 TVControl.prototype.constructor = TVControl;  
 TVControl.prototype.eventHandlers.onLaunch = function (launchRequest, session, response) {  
   var speechText = "Welcome to the Television Controller. You can issue commands like start netflix or mute? ... Now, what can I help you with.";  
   // If the user either does not reply to the welcome message or says something that is not  
   // understood, they will be prompted again with this text.  
   var repromptText = "For instructions on what you can say, please say help me.";  
   response.ask(speechText, repromptText);  
 };  
 function executeCommand(response, itemName){  
  var cardTitle = "command for " + itemName,  
       recipe = recipes[itemName],  
       speechOutput,  
       repromptOutput;  
     if (recipe) {  
       speechOutput = {  
         speech: recipe,  
         type: AlexaSkill.speechOutputType.PLAIN_TEXT  
       };  
       var iotdata = new AWS.IotData({endpoint: 'axxxxxxxxxxxxx.iot.us-east-1.amazonaws.com'});  
       var iotpayload = {  
         topic: 'bravia/command',  
         payload: recipe,  
         qos: 0  
       };  
       console.log("Sent Data:", JSON.stringify(iotpayload,null,2));  
       iotdata.publish(iotpayload, function(err, data){  
         if(err){  
           console.log(err);  
         }  
         else{  
           console.log("success?");  
         }  
         response.tellWithCard(speechOutput, cardTitle, recipe);  
       });  
     } else {  
       var speech;  
       if (itemName) {  
         speech = "I'm sorry, there is no command to " + itemName + ". What else can I help with?";  
       } else {  
         speech = "I'm sorry, I currently do not know that command. What else can I help with?";  
       }  
       speechOutput = {  
         speech: speech,  
         type: AlexaSkill.speechOutputType.PLAIN_TEXT  
       };  
       repromptOutput = {  
         speech: "What else can I help with?",  
         type: AlexaSkill.speechOutputType.PLAIN_TEXT  
       };  
       response.ask(speechOutput, repromptOutput);  
     }    
 }  
 TVControl.prototype.intentHandlers = {  
   "CommandIntent": function (intent, session, response) {  
     var itemSlot = intent.slots.Action,  
       itemName;  
     console.log(itemSlot);  
     if (itemSlot && itemSlot.value){  
       itemName = itemSlot.value.toLowerCase();  
     }  
     executeCommand(response, itemName);  
   },  
   "InputIntent": function (intent, session, response) {  
     var itemSlot = intent.slots.InputSwitch,  
       itemName;  
     console.log(itemSlot);  
     if (itemSlot && itemSlot.value){  
       itemName = itemSlot.value.toLowerCase();  
     }  
     executeCommand(response, itemName);  
   },  
   "ChannelIntent": function (intent, session, response) {  
     var itemSlot = intent.slots.ChannelSwitch,  
       itemName;  
     console.log(intent);  
     if (itemSlot && itemSlot.value){  
       itemName = itemSlot.value.toLowerCase();  
     }  
     executeCommand(response, itemName);  
   },  
   "AMAZON.StopIntent": function (intent, session, response) {  
     var speechOutput = "Goodbye";  
     response.tell(speechOutput);  
   },  
   "AMAZON.CancelIntent": function (intent, session, response) {  
     var speechOutput = "Goodbye";  
     response.tell(speechOutput);  
   },  
   "AMAZON.HelpIntent": function (intent, session, response) {  
     var speechText = "You can issue commands to your t. v., such as tell my t. v. to shut off, or, you can say exit... Now, what can I help you with?";  
     var repromptText = "You can say things like, what's the recipe for a chest, or you can say exit... Now, what can I help you with?";  
     var speechOutput = {  
       speech: speechText,  
       type: AlexaSkill.speechOutputType.PLAIN_TEXT  
     };  
     var repromptOutput = {  
       speech: repromptText,  
       type: AlexaSkill.speechOutputType.PLAIN_TEXT  
     };  
     response.ask(speechOutput, repromptOutput);  
   }  
 };  
 exports.handler = function (event, context) {  
   var tvControl = new TVControl();  
   tvControl.execute(event, context);  
 };  

There are increase items in intent with InputIntent and ChannelIntent which allow it on Alexa Skill. InputIntent is for switching HDMIs and ChannelIntent for changing channels.

commands.js
 /**  
   Copyright 2014-2015 Amazon.com, Inc. or its affiliates. All Rights Reserved.  
   Licensed under the Apache License, Version 2.0 (the "License"). You may not use this file except in compliance with the License. A copy of the License is located at  
     http://aws.amazon.com/apache2.0/  
   or in the "license" file accompanying this file. This file is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License.  
 */  
 module.exports = {  
      "channel up" : "ChannelUp",  
      "channel down" : "ChannelDown",  
      "turn up the volume" : "VolumeUp",  
      "get louder" : "VolumeUp",  
      "turn down the volume" : "VolumeDown",  
      "get quieter" : "VolumeDown",  
      "shut up" : "Mute",  
      "mute" : "Mute",  
      "silence" : "Mute",  
      "chill" : "Mute",  
      "pause" : "Pause",  
      "freeze" : "Pause",  
      "hold up" : "Pause",  
      "stop" : "Stop",  
      "halt" : "Stop",  
      "press play" : "Play",  
      "resume playing" : "Play",  
      "turn on subtitles" : "Subtitle",  
      "enable subtitles" : "Subtitle",  
      "subtitles" : "Subtitle",  
      "start netflix" : "Netflix",  
      "netflix and chill" : "Netflix",  
      "turn off": "PowerOff",  
      "shut off": "PowerOff",  
      "input": "Input",  
      "source": "Input",  
      "enter": "Enter",  
      "ok": "Enter",  
      "left": "Left",  
      "right": "Right",  
      "up": "Up",  
      "down": "Down",  
      "exit": "Exit",  
      "return": "Return",  
      "go back": "Return",  
      "go home": "Home",  
      "home": "Home",  
      "cable":"Hdmi1",  
      "playstation":"Hdmi4",  
      "fire stick":"Hdmi1",  
      "amazon Stick":"Hdmi1",  
      "blu ray":"Hdmi3",  
      "play station":"",  
      "hdmi 1":"Hdmi1",  
      "hdmi 2":"Hdmi2",  
      "hdmi 3":"Hdmi3",  
      "hdmi 4":"Hdmi4",  
      "tatasky welcome":"Num1,Num0,Num0",  
      "tatasky bollywood premiere hd":"Num3,Num0,Num4",  
      "tatasky kids cinema ":"Num3,Num0,Num6",  
      "showcase 1 hd":"Num4,Num0,Num1",  
      "showcase 2 hd":"Num4,Num0,Num2",  
      "showcase 3 hd":"Num4,Num0,Num3",  
      "showcase 4 hd":"Num4,Num0,Num4",  
      "showcase 5 hd":"Num4,Num0,Num5",  
      "dd national":"Num1,Num12,Num1,Num4",  
      "star plus hd":"Num1,Num12,Num1,Num5",  
      "star plus hd plus ":"Num1,Num12,Num1,Num6",  
      "star plus plus ":"Num1,Num12,Num1,Num6",  
      "star plus":"Num1,Num12,Num1,Num7",  
      "life ok hd":"Num1,Num2,Num0",  
      "life ok":"Num1,Num2,Num2",  
      "sony hd ":"Num1,Num2,Num8",  
      "sony hd plus":"Num1,Num2,Num9",  
      "sony plus":"Num1,Num2,Num9",  
      "sony ":"Num1,Num3,Num0",  
      "sony sab hd":"Num1,Num3,Num2",  
      "sony sab":"Num1,Num3,Num4",  
      "and tv hd":"Num1,Num3,Num7",  
      "and tv":"Num1,Num3,Num9",  
      "zee tv hd":"Num1,Num4,Num1",  
      "zee tv hd plus ":"Num1,Num4,Num2",  
      "zee tv plus":"Num1,Num4,Num2",  
      "zee tv":"Num1,Num4,Num3",  
      "colors hd":"Num1,Num4,Num7",  
      "colors hd plus":"Num1,Num4,Num8",  
      "colors plus ":"Num1,Num4,Num8",  
      "colors":"Num1,Num4,Num9",  
      "bindass":"Num1,Num5,Num3",  
      "star utsav":"Num1,Num5,Num4",  
      "zee anmol":"Num1,Num5,Num5",  
      "rishtey":"Num1,Num5,Num7",  
      "sony pal":"Num1,Num5,Num8",  
      "epic hd":"Num7,Num3,Num4",  
      "epic":"Num1,Num6,Num1",  
      "id":"Num1,Num6,Num2",  
      "big magic":"Num1,Num6,Num6",  
      "dd kisan":"Num1,Num9,Num8",  
      "dd india":"Num1,Num9,Num9",  
      "shop cj":"Num1,Num8,Num0",  
      "home shop 18":"Num1,Num8,Num2",  
      "gemporia":"Num1,Num8,Num8",  
      "naptol blue":"Num1,Num9,Num0",  
      "the qyou hd":"Num2,Num0,Num0",  
      "the qyou ":"Num2,Num0,Num1",  
      "star world hd":"Num2,Num0,Num4",  
      "star world":"Num2,Num0,Num6",  
      "star world premiere hd":"Num2,Num0,Num8",  
      "axn hd":"Num2,Num1,Num3",  
      "axn":"Num2,Num1,Num5",  
      "zee cafe hd":"Num2,Num1,Num8",  
      "zee cafe":"Num2,Num2,Num0",  
      "colors infinity hd":"Num2,Num12,Num2,Num4",  
      "colors infinity":"Num2,Num12,Num2,Num6",  
      "comedy central hd":"Num2,Num12,Num2,Num9",  
      "comedy central":"Num2,Num3,Num0",  
      "star gold hd":"Num3,Num0,Num8",  
      "star gold hd plus ":"Num3,Num0,Num9",  
      "star gold plus ":"Num3,Num0,Num9",  
      "star gold":"Num3,Num1,Num0",  
      "sony max hd":"Num3,Num1,Num2",  
      "sony max hd plus ":"Num3,Num1,Num3",  
      "sony max plus":"Num3,Num1,Num3",  
      "sony max":"Num3,Num1,Num4",  
      "movies ok":"Num3,Num1,Num7",  
      "zee cinema hd":"Num3,Num1,Num9",  
      "zee cinema hd plus ":"Num3,Num2,Num0",  
      "zee cinema plus":"Num3,Num2,Num0",  
      "zee cinema":"Num3,Num2,Num1",  
      "utv movies":"Num3,Num2,Num3",  
      "utv action":"Num3,Num2,Num5",  
      "zee classic":"Num3,Num2,Num7",  
      "and pictures hd":"Num3,Num3,Num1",  
      "and pictures":"Num3,Num12,Num3,Num3",  
      "zee action":"Num3,Num12,Num3,Num5",  
      "b4u movies":"Num3,Num12,Num3,Num7",  
      "sony max 2":"Num3,Num12,Num3,Num8",  
      "star gold select hd":"Num3,Num12,Num3,Num9",  
      "star gold select ":"Num3,Num4,Num0",  
      "cineplex hd":"Num3,Num4,Num1",  
      "cinema tv":"Num3,Num4,Num2",  
      "wow cinema":"Num3,Num4,Num3",  
      "enterr10":"Num3,Num4,Num4",  
      "bflix movies":"Num3,Num4,Num5",  
      "star utsav movies ":"Num3,Num4,Num7",  
      "rishtey cineplex ":"Num3,Num4,Num8",  
      "sony wah ":"Num3,Num4,Num9",  
      "zee anmol cinema ":"Num3,Num5,Num0",  
      "skystar ":"Num3,Num5,Num1",  
      "multiplex ":"Num3,Num5,Num2",  
      "star movies hd":"Num3,Num5,Num4",  
      "star movies":"Num3,Num5,Num5",  
      "sony pix hd":"Num3,Num5,Num9",  
      "sony pix":"Num3,Num6,Num0",  
      "hbo hd":"Num3,Num6,Num3",  
      "hbo":"Num3,Num6,Num4",  
      "zee studio hd":"Num3,Num6,Num5",  
      "zee studio":"Num3,Num6,Num6",  
      "wb":"Num3,Num6,Num9",  
      "star movies select hd ":"Num3,Num7,Num0",  
      "movies now hd":"Num3,Num7,Num6",  
      "movies now":"Num3,Num7,Num7",  
      "mn plus hd ":"Num3,Num7,Num8",  
      "mnx hd":"Num3,Num8,Num0",  
      "mnx":"Num3,Num8,Num1",  
      "romedy now hd":"Num3,Num8,Num2",  
      "romedy now":"Num3,Num8,Num3",  
      "sony le plex hd":"Num3,Num8,Num4",  
      "dd sports":"Num4,Num5,Num3",  
      "star sports 1 hd ":"Num4,Num5,Num4",  
      "star sports 1":"Num4,Num5,Num5",  
      "star sports 2 hd ":"Num4,Num5,Num6",  
      "star sports 2":"Num4,Num5,Num7",  
      "star sports 1 hd hindi":"Num4,Num5,Num9",  
      "star sports 1 hindi":"Num4,Num6,Num0",  
      "star sports select 1 hd ":"Num4,Num6,Num3",  
      "star sports select 1 ":"Num4,Num6,Num4",  
      "star sports select 2 hd ":"Num4,Num6,Num5",  
      "star sports select 2 ":"Num4,Num6,Num6",  
      "sony ten 1 hd":"Num4,Num7,Num0",  
      "sony ten 1":"Num4,Num7,Num1",  
      "sony ten 2 hd":"Num4,Num7,Num3",  
      "sony ten 2":"Num4,Num7,Num4",  
      "sony ten 3 hd":"Num4,Num7,Num5",  
      "sony ten 3":"Num4,Num7,Num6",  
      "sony ten golf hd ":"Num4,Num7,Num7",  
      "sony six hd":"Num4,Num8,Num3",  
      "sony six":"Num4,Num8,Num4",  
      "sony espn hd":"Num4,Num8,Num5",  
      "sony espn":"Num4,Num8,Num6",  
      "neo sports":"Num4,Num9,Num2",  
      "neo prime":"Num4,Num9,Num3",  
      "d sports hd":"Num4,Num9,Num5",  
      "dd news":"Num5,Num0,Num2",  
      "abp news":"Num5,Num0,Num4",  
      "ndtv india":"Num5,Num0,Num6",  
      "aaj tak":"Num5,Num0,Num9",  
      "zee news":"Num5,Num1,Num1",  
      "india tv":"Num5,Num1,Num4",  
      "news 24":"Num5,Num1,Num6",  
      "news18 india":"Num5,Num1,Num9",  
      "zee hindustan":"Num5,Num2,Num0",  
      "india news":"Num5,Num2,Num1",  
      "news nation":"Num5,Num2,Num3",  
      "news world india":"Num5,Num2,Num5",  
      "india news haryana":"Num5,Num3,Num0",  
      "samachar plus":"Num5,Num3,Num2",  
      "sudarshan news":"Num5,Num3,Num4",  
      "kashish news":"Num5,Num3,Num6",  
      "ltv":"Num5,Num3,Num8",  
      "sadhna plus":"Num5,Num4,Num0",  
      "apn":"Num5,Num4,Num2",  
      "etv news haryana hp":"Num5,Num4,Num4",  
      "sadhna prime news":"Num5,Num4,Num6",  
      "mh one news":"Num5,Num4,Num8",  
      "india news up uk":"Num5,Num12,Num5,Num0",  
      "news state up uk":"Num5,Num12,Num5,Num2",  
      "india news mp ch":"Num5,Num12,Num5,Num4",  
      "india news rajasthan":"Num5,Num12,Num5,Num6",  
      "samay":"Num5,Num6,Num0",  
      "janta tv":"Num5,Num6,Num2",  
      "samay rajasthan":"Num5,Num6,Num4",  
      "samay up uk":"Num5,Num6,Num8",  
      "k news":"Num5,Num7,Num0",  
      "samay mp ch":"Num5,Num7,Num2",  
      "samay bihar jharkhand":"Num5,Num7,Num6",  
      "jk 24x7 news":"Num5,Num7,Num8",  
      "news 11":"Num5,Num7,Num9",  
      "news 1 india":"Num5,Num8,Num0",  
      "patrika tv rajasthan ":"Num5,Num8,Num1",  
      "swaraj express smbc ":"Num5,Num8,Num2",  
      "hindi khabar":"Num5,Num8,Num3",  
      "yo tv ":"Num5,Num8,Num4",  
      "total tv ":"Num5,Num8,Num5",  
      "news india ":"Num5,Num8,Num6",  
      "bharat samachar ":"Num5,Num8,Num7",  
      "hnn 24×7 ":"Num5,Num8,Num8",  
      "khabarain abhi tak ":"Num5,Num8,Num9",  
      "zee business":"Num5,Num9,Num4",  
      "cnbc awaaz":"Num5,Num9,Num5",  
      "lok sabha tv":"Num5,Num9,Num8",  
      "rajya sabha":"Num5,Num9,Num9",  
      "ndtv 24x7":"Num6,Num0,Num4",  
      "times now hd":"Num6,Num0,Num5",  
      "times now":"Num6,Num0,Num6",  
      "india today":"Num6,Num0,Num9",  
      "cnn news18":"Num6,Num1,Num1",  
      "newsx hd":"Num6,Num1,Num2",  
      "newsx":"Num6,Num1,Num3",  
      "mirror now":"Num6,Num1,Num4",  
      "wion":"Num6,Num1,Num5",  
      "republic tv ":"Num6,Num1,Num6",  
      "ndtv profit-prime":"Num6,Num2,Num1",  
      "cnbc tv18 prime hd":"Num6,Num2,Num2",  
      "cnbc tv18":"Num6,Num2,Num3",  
      "btvi":"Num6,Num2,Num4",  
      "et now":"Num6,Num2,Num5",  
      "news 9":"Num6,Num2,Num6",  
      "cnn intl":"Num6,Num3,Num1",  
      "bbc world":"Num6,Num3,Num3",  
      "al jazeera":"Num6,Num3,Num5",  
      "channel newsasia":"Num6,Num3,Num6",  
      "france 24":"Num6,Num3,Num7",  
      "tv5 monde asie":"Num6,Num3,Num8",  
      "dw":"Num6,Num3,Num9",  
      "australia plus":"Num6,Num4,Num0",  
      "russia today":"Num6,Num4,Num1",  
      "nhk world tv":"Num6,Num4,Num2",  
      "hungama":"Num6,Num5,Num5",  
      "disney xd":"Num6,Num5,Num7",  
      "disney channel":"Num6,Num5,Num9",  
      "nick hd plus":"Num6,Num12,Num6,Num2",  
      "nick":"Num6,Num12,Num6,Num3",  
      "cartoon network":"Num6,Num12,Num6,Num6",  
      "pogo":"Num6,Num7,Num0",  
      "discovery kids":"Num6,Num7,Num2",  
      "sonic":"Num6,Num7,Num4",  
      "baby tv hd":"Num6,Num7,Num6",  
      "baby tv":"Num6,Num7,Num7",  
      "nick jr.":"Num6,Num8,Num0",  
      "disney jr.":"Num6,Num8,Num1",  
      "toonami":"Num6,Num8,Num5",  
      "sony yay":"Num6,Num8,Num6",  
      "national geographic hd":"Num7,Num0,Num8",  
      "national geographic":"Num7,Num0,Num9",  
      "nat geo wild hd":"Num7,Num1,Num1",  
      "nat geo wild":"Num7,Num1,Num2",  
      "discovery hd":"Num7,Num1,Num3",  
      "discovery ":"Num7,Num1,Num4",  
      "animal planet hd":"Num7,Num1,Num6",  
      "animal planet":"Num7,Num1,Num7",  
      "discovery science":"Num7,Num1,Num9",  
      "history tv18 hd":"Num7,Num2,Num0",  
      "history tv18":"Num7,Num2,Num1",  
      "fyi tv18":"Num7,Num2,Num3",  
      "sony bbc earth hd":"Num7,Num2,Num4",  
      "sony bbc earth ":"Num7,Num2,Num5",  
      "fox life hd":"Num7,Num5,Num3",  
      "fox life":"Num7,Num5,Num4",  
      "green tv":"Num7,Num5,Num5",  
      "nat geo people hd":"Num7,Num5,Num6",  
      "nat geo people":"Num7,Num5,Num7",  
      "tlc hd":"Num7,Num5,Num9",  
      "tlc":"Num7,Num6,Num0",  
      "ndtv good times":"Num7,Num6,Num2",  
      "discovery turbo":"Num7,Num6,Num4",  
      "travel xp hd":"Num7,Num6,Num5",  
      "travel xp":"Num7,Num6,Num6",  
      "food food":"Num7,Num6,Num8",  
      "living foodz":"Num7,Num6,Num9",  
      "ftv india":"Num7,Num12,Num7,Num2",  
      "care world":"Num7,Num12,Num7,Num3",  
      "mtv hd plus":"Num8,Num0,Num6",  
      "mtv":"Num8,Num0,Num7",  
      "9xm":"Num8,Num0,Num9",  
      "zoom":"Num8,Num1,Num1",  
      "e24":"Num8,Num1,Num3",  
      "bindass play":"Num8,Num1,Num4",  
      "sony mix":"Num8,Num1,Num6",  
      "mtv beats hd":"Num8,Num2,Num0",  
      "mtv beats":"Num8,Num2,Num1",  
      "b4u music":"Num8,Num2,Num2",  
      "zee etc bollywood":"Num8,Num2,Num4",  
      "mastiii":"Num8,Num2,Num5",  
      "zing":"Num8,Num2,Num6",  
      "channel v":"Num8,Num2,Num8",  
      "music india":"Num8,Num2,Num9",  
      "9x jalwa":"Num8,Num3,Num0",  
      "sony rox hd ":"Num8,Num3,Num1",  
      "vh1 hd":"Num8,Num5,Num5",  
      "vh1":"Num8,Num5,Num6",  
      "nat geo music hd":"Num8,Num5,Num8",  
      "nat geo music":"Num8,Num5,Num9",  
      "colors oriya":"Num1,Num7,Num5,Num4",  
      "tarang tv":"Num1,Num7,Num5,Num7",  
      "news world odisha":"Num1,Num7,Num5,Num8",  
      "tarang music":"Num1,Num7,Num5,Num9",  
      "otv":"Num1,Num7,Num6,Num1",  
      "sarthak tv":"Num1,Num7,Num6,Num5",  
      "prarthana tv":"Num1,Num7,Num6,Num6",  
      "zee kalinga news":"Num1,Num7,Num6,Num8",  
      "kanak news":"Num1,Num7,Num7,Num0",  
      "kalinga tv":"Num1,Num7,Num7,Num2",  
      "prameya news 7":"Num1,Num7,Num7,Num3",  
      "etv news odia":"Num1,Num7,Num7,Num4",  
      "alankar":"Num1,Num7,Num7,Num5",  
      "dd odia":"Num1,Num7,Num9,Num9",  
 };  

The TV channels are mapped as per Tata Sky India. There was an issue like when 115 has to be pressed the immediate one 11 does not work. So, to delay I have added Num12 in all those places, if it works without it for you then it could be removed, and it is just happening for first two similar number whereas 511 works just fine.

AlexaSkill.js
 /**  
   Copyright 2014-2015 Amazon.com, Inc. or its affiliates. All Rights Reserved.  
   Licensed under the Apache License, Version 2.0 (the "License"). You may not use this file except in compliance with the License. A copy of the License is located at  
     http://aws.amazon.com/apache2.0/  
   or in the "license" file accompanying this file. This file is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License.  
 */  
 'use strict';  
 function AlexaSkill(appId) {  
   this._appId = appId;  
 }  
 AlexaSkill.speechOutputType = {  
   PLAIN_TEXT: 'PlainText',  
   SSML: 'SSML'  
 }  
 AlexaSkill.prototype.requestHandlers = {  
   LaunchRequest: function (event, context, response) {  
     this.eventHandlers.onLaunch.call(this, event.request, event.session, response);  
   },  
   IntentRequest: function (event, context, response) {  
     this.eventHandlers.onIntent.call(this, event.request, event.session, response);  
   },  
   SessionEndedRequest: function (event, context) {  
     this.eventHandlers.onSessionEnded(event.request, event.session);  
     context.succeed();  
   }  
 };  
 /**  
  * Override any of the eventHandlers as needed  
  */  
 AlexaSkill.prototype.eventHandlers = {  
   /**  
    * Called when the session starts.  
    * Subclasses could have overriden this function to open any necessary resources.  
    */  
   onSessionStarted: function (sessionStartedRequest, session) {  
   },  
   /**  
    * Called when the user invokes the skill without specifying what they want.  
    * The subclass must override this function and provide feedback to the user.  
    */  
   onLaunch: function (launchRequest, session, response) {  
     throw "onLaunch should be overriden by subclass";  
   },  
   /**  
    * Called when the user specifies an intent.  
    */  
   onIntent: function (intentRequest, session, response) {  
     var intent = intentRequest.intent,  
       intentName = intentRequest.intent.name,  
       intentHandler = this.intentHandlers[intentName];  
     if (intentHandler) {  
       console.log('dispatch intent = ' + intentName);  
       intentHandler.call(this, intent, session, response);  
     } else {  
       throw 'Unsupported intent = ' + intentName;  
     }  
   },  
   /**  
    * Called when the user ends the session.  
    * Subclasses could have overriden this function to close any open resources.  
    */  
   onSessionEnded: function (sessionEndedRequest, session) {  
   }  
 };  
 /**  
  * Subclasses should override the intentHandlers with the functions to handle specific intents.  
  */  
 AlexaSkill.prototype.intentHandlers = {};  
 AlexaSkill.prototype.execute = function (event, context) {  
   try {  
     console.log("session applicationId: " + event.session.application.applicationId);  
     // Validate that this request originated from authorized source.  
     if (this._appId && event.session.application.applicationId !== this._appId) {  
       console.log("The applicationIds don't match : " + event.session.application.applicationId + " and "  
         + this._appId);  
       throw "Invalid applicationId";  
     }  
     if (!event.session.attributes) {  
       event.session.attributes = {};  
     }  
     if (event.session.new) {  
       this.eventHandlers.onSessionStarted(event.request, event.session);  
     }  
     // Route the request to the proper handler which may have been overriden.  
     var requestHandler = this.requestHandlers[event.request.type];  
     requestHandler.call(this, event, context, new Response(context, event.session));  
   } catch (e) {  
     console.log("Unexpected exception " + e);  
     context.fail(e);  
   }  
 };  
 var Response = function (context, session) {  
   this._context = context;  
   this._session = session;  
 };  
 function createSpeechObject(optionsParam) {  
   if (optionsParam && optionsParam.type === 'SSML') {  
     return {  
       type: optionsParam.type,  
       ssml: optionsParam.speech  
     };  
   } else {  
     return {  
       type: optionsParam.type || 'PlainText',  
       text: optionsParam.speech || optionsParam  
     }  
   }  
 }  
 Response.prototype = (function () {  
   var buildSpeechletResponse = function (options) {  
     var alexaResponse = {  
       outputSpeech: createSpeechObject(options.output),  
       shouldEndSession: options.shouldEndSession  
     };  
     if (options.reprompt) {  
       alexaResponse.reprompt = {  
         outputSpeech: createSpeechObject(options.reprompt)  
       };  
     }  
     if (options.cardTitle && options.cardContent) {  
       alexaResponse.card = {  
         type: "Simple",  
         title: options.cardTitle,  
         content: options.cardContent  
       };  
     }  
     var returnResult = {  
         version: '1.0',  
         response: alexaResponse  
     };  
     if (options.session && options.session.attributes) {  
       returnResult.sessionAttributes = options.session.attributes;  
     }  
     return returnResult;  
   };  
   return {  
     tell: function (speechOutput) {  
       this._context.succeed(buildSpeechletResponse({  
         session: this._session,  
         output: speechOutput,  
         shouldEndSession: true  
       }));  
     },  
     tellWithCard: function (speechOutput, cardTitle, cardContent) {  
       this._context.succeed(buildSpeechletResponse({  
         session: this._session,  
         output: speechOutput,  
         cardTitle: cardTitle,  
         cardContent: cardContent,  
         shouldEndSession: true  
       }));  
     },  
     ask: function (speechOutput, repromptSpeech) {  
       this._context.succeed(buildSpeechletResponse({  
         session: this._session,  
         output: speechOutput,  
         reprompt: repromptSpeech,  
         shouldEndSession: false  
       }));  
     },  
     askWithCard: function (speechOutput, repromptSpeech, cardTitle, cardContent) {  
       this._context.succeed(buildSpeechletResponse({  
         session: this._session,  
         output: speechOutput,  
         reprompt: repromptSpeech,  
         cardTitle: cardTitle,  
         cardContent: cardContent,  
         shouldEndSession: false  
       }));  
     }  
   };  
 })();  
 module.exports = AlexaSkill;  

NOTE: Alexa Skill Kit and AWS IoT has to be enabled for triggers. Do enable CloudTrail for checking logs in case of any issues. If you get any issue related to permission/authorization than you could enable it for all by composing IAM role manually.

Alexa Skill Kit

Intent Schema 
 {  
  "intents": [  
   {  
    "slots": [  
     {  
      "name": "Action",  
      "type": "LIST_OF_ACTIONS"  
     }  
    ],  
    "intent": "CommandIntent"  
   },  
   {  
    "slots": [  
     {  
      "name": "InputSwitch",  
      "type": "INPUT_SWITCH"  
     }  
    ],  
    "intent": "InputIntent"  
   },  
   {  
    "slots": [  
     {  
      "name": "ChannelSwitch",  
      "type": "CHANNEL_SWITCH"  
     }  
    ],  
    "intent": "ChannelIntent"  
   },  
   {  
    "intent": "AMAZON.HelpIntent"  
   },  
   {  
    "intent": "AMAZON.StopIntent"  
   },  
   {  
    "intent": "AMAZON.CancelIntent"  
   }  
  ]  
 }  

Sample Utterances 
 CommandIntent please {Action}  
 CommandIntent {Action} my t. v.  
 CommandIntent {Action} my television  
 CommandIntent {Action} my bravia  
 CommandIntent to press {Action}  
 CommandIntent to {Action}  
 CommandIntent {Action}  
 InputIntent switch to {InputSwitch}  
 ChannelIntent change to {ChannelSwitch}  
 ChannelIntent change channel to {ChannelSwitch}  

Custom Slot Types

LIST_OF_ACTIONS
--------------------------
Channel Up
Channel Down
Turn up the volume
Get louder
turn down the volume
Get quieter
shut up
mute
silence
chill
pause
freeze
stop
halt
Press play
resume playing
turn on subtitles
start netflix
netflix and chill
turn off
shut off

INPUT_SWITCH
----------------------
Cable
Playstation
Fire Stick
Amazon Stick
Blu ray
Play station
HDMI 1
HDMI 2
HDMI 3
HDMI 4

CHANNEL_SWITCH
---------------------------
Tatasky Welcome
Tatasky Bollywood Premiere HD
Tatasky Kids Cinema
SHOWCASE 1 Hd
SHOWCASE 2 Hd
SHOWCASE 3 Hd
SHOWCASE 4 Hd
SHOWCASE 5 Hd
DD National
STAR PLUS Hd
STAR PLUS  Hd  Plus
STAR PLUS Plus
STAR PLUS
Life OK Hd
Life OK
SONY Hd
SONY Hd  Plus
SONY Plus
SONY
SONY SAB Hd
SONY SAB
And TV Hd
And TV
Zee TV Hd
Zee TV Hd  Plus
Zee TV Plus
Zee TV
Colors Hd
Colors Hd  Plus
Colors Plus
Colors
Bindass
STAR Utsav
Zee Anmol
Rishtey
SONY PAL
EPIC Hd
EPIC
ID
Big Magic
DD Kisan
DD India
Shop CJ
Home Shop 18
Gemporia
Naptol Blue
The QYOU Hd
The QYOU
STAR WORLD Hd
STAR WORLD
STAR WORLD PREMIERE Hd
AXN Hd
AXN
Zee Cafe Hd
Zee Cafe
Colors Infinity Hd
Colors Infinity
Comedy Central Hd
Comedy Central
STAR GOLD Hd
STAR GOLD Hd  Plus
STAR GOLD  Plus
STAR GOLD
SONY MAX Hd
SONY MAX Hd  Plus
SONY MAX Plus
SONY MAX
Movies OK
Zee Cinema Hd
Zee Cinema Hd  Plus
Zee Cinema Plus
Zee Cinema
UTV Movies
UTV Action
Zee Classic
And pictures Hd
And pictures
Zee Action
B4U Movies
SONY MAX 2
Star Gold Select Hd
Star Gold Select
Cineplex Hd
Cinema TV
WoW Cinema
Enterr10
Bflix Movies
Star Utsav Movies
rishtey Cineplex
Sony Wah
Zee Anmol Cinema
SKYSTAR
Multiplex
STAR Movies Hd
STAR Movies
SONY PIX Hd
SONY PIX
HBO Hd
HBO
Zee Studio Hd
Zee Studio
WB
STAR Movies Select Hd
Movies NOW Hd
Movies NOW
MN Plus Hd
MNX Hd
MNX
Romedy NOW Hd
Romedy NOW
SONY Le Plex Hd
DD Sports
STAR Sports 1 Hd
STAR Sports 1
STAR Sports 2 Hd
STAR Sports 2
STAR Sports 1 Hd  Hindi
STAR Sports 1 Hindi
STAR Sports Select 1 Hd
STAR Sports Select 1
STAR Sports Select 2 Hd
Star Sports Select 2
SONY TEN 1 Hd
SONY TEN 1
SONY TEN 2 Hd
SONY TEN 2
SONY TEN 3 Hd
SONY TEN 3
SONY TEN Golf Hd
SONY SIX Hd
SONY SIX
SONY ESPN Hd
SONY ESPN
Neo Sports
Neo Prime
D SPORTS Hd
DD News
ABP News
NDTV India
Aaj Tak
Zee News
INDIA TV
News 24
News18 India
Zee Hindustan
India News
News Nation
News World India
India News Haryana
Samachar Plus
Sudarshan News
Kashish News
LTV
Sadhna Plus
APN
ETV News Haryana HP
Sadhna Prime News
MH One News
India News UP UK
News State UP UK
India News MP CH
India News Rajasthan
Samay
Janta TV
Samay Rajasthan
Samay UP UK
K News
Samay MP CH
Samay Bihar Jharkhand
JK 24x7 News
News 11
News 1 India
Patrika TV Rajasthan
Swaraj Express SMBC
Hindi Khabar
YO TV
Total TV
News India
Bharat Samachar
HNN 24×7
Khabarain Abhi Tak
Zee Business
CNBC Awaaz
Lok Sabha TV
Rajya Sabha
NDTV 24X7
TIMES NOW Hd
TIMES NOW
INDIA TODAY
CNN News18
NewsX Hd
NewsX
Mirror NOW
WION
Republic TV
NDTV Profit-Prime
CNBC TV18 Prime Hd
CNBC TV18
BTVi
ET NOW
News 9
CNN Intl
BBC World
Al Jazeera
Channel NewsAsia
France 24
TV5 Monde Asie
DW
Australia Plus
Russia Today
NHK World TV
Hungama
Disney XD
Disney Channel
NICK Hd Plus
NICK
Cartoon Network
Pogo
Discovery Kids
Sonic
Baby TV Hd
Baby TV
Nick Jr
Disney Jr
Toonami
SONY YAY
National Geographic Hd
National Geographic
Nat Geo Wild Hd
Nat Geo Wild
Discovery Hd
Discovery
Animal Planet Hd
Animal Planet
Discovery Science
History TV18 Hd
History TV18
FYI TV18
SONY BBC Earth Hd
SONY BBC Earth
FOX Life Hd
FOX Life
Green TV
Nat Geo People Hd
Nat Geo People
TLC Hd
TLC
NDTV GOOD TIMES
Discovery Turbo
Travel XP Hd
Travel XP
Food Food
Living Foodz
FTV India
Care World
MTV Hd Plus
MTV
9XM
Zoom
E24
BINDASS PLAY
Sony MIX
MTV Beats Hd
MTV Beats
B4U Music
Zee ETC Bollywood
Mastiii
Zing
Channel V
Music India
9X Jalwa
SONY ROX Hd
VH1 Hd
VH1
Nat Geo Music Hd
Nat Geo Music
Colors Oriya
Tarang TV
News World Odisha
Tarang Music
OTV
Sarthak TV
Prarthana TV
Zee Kalinga News
Kanak News
Kalinga TV
Prameya News 7
ETV News Odia
Alankar
DD Odia

NOTE: This is my first attempt on Python and Lambda codes, it might not be good.

Comments

Popular posts from this blog

Elegantly dealing with TimeZones in MVC Core / WebApi

In any new application handling TimeZone/DateTime is mostly least priority and generally, if someone is concerned then it would be handled by using DateTime.UtcNow on codes while creating current dates and converting incoming Date to UTC to save on servers. Basically, the process is followed by saving DateTime to UTC format in a database and keep converting data to native format based on user region or single region in the application's presentation layer. The above is tedious work and have to be followed religiously. If any developer misses out the manual conversion, then that area of code/view would not work. With newer frameworks, there are flexible ways to deal/intercept incoming or outgoing calls to simplify conversion of TimeZones. These are steps/process to achieve it. 1. Central code for storing user's state about TimeZone. Also, central code for conversion logic based on TimeZones. 2. Dependency injection for the above class to be able to use global

Using Redis distributed cache in dotnet core with helper extension methods

Redis cache is out process cache provider for a distributed environment. It is popular in Azure Cloud solution, but it also has a standalone application to operate upon in case of small enterprises application. How to install Redis Cache on a local machine? Redis can be used as a local cache server too on our local machines. At first install, Chocolatey https://chocolatey.org/ , to make installation of Redis easy. Also, the version under Chocolatey supports more commands and compatible with Official Cache package from Microsoft. After Chocolatey installation hit choco install redis-64 . Once the installation is done, we can start the server by running redis-server . Distributed Cache package and registration dotnet core provides IDistributedCache interface which can be overrided with our own implementation. That is one of the beauties of dotnet core, having DI implementation at heart of framework. There is already nuget package available to override IDistributedCache i

Making FluentValidation compatible with Swagger including Enum or fixed List support

FluentValidation is not directly compatible with Swagger API to validate models. But they do provide an interface through which we can compose Swagger validation manually. That means we look under FluentValidation validators and compose Swagger validator properties to make it compatible. More of all mapping by reading information from FluentValidation and setting it to Swagger Model Schema. These can be done on any custom validation from FluentValidation too just that proper schema property has to be available from Swagger. Custom validation from Enum/List values on FluentValidation using FluentValidation.Validators; using System.Collections.Generic; using System.Linq; using static System.String; /// <summary> /// Validator as per list of items. /// </summary> /// <seealso cref="PropertyValidator" /> public class FixedListValidator : PropertyValidator { /// <summary> /// Gets the valid items /// <

Handling JSON DateTime format on Asp.Net Core

This is a very simple trick to handle JSON date format on AspNet Core by global settings. This can be applicable for the older version as well. In a newer version by default, .Net depends upon Newtonsoft to process any JSON data. Newtonsoft depends upon Newtonsoft.Json.Converters.IsoDateTimeConverter class for processing date which in turns adds timezone for JSON data format. There is a global setting available for same that can be adjusted according to requirement. So, for example, we want to set default formatting to US format, we just need this code. services.AddMvc() .AddJsonOptions(options => { options.SerializerSettings.DateTimeZoneHandling = "MM/dd/yyyy HH:mm:ss"; });

Kendo MVC Grid DataSourceRequest with AutoMapper - Advance

The actual process to make DataSourceRequest compatible with AutoMapper was explained in my previous post  Kendo MVC Grid DataSourceRequest with AutoMapper , where we had created custom model binder attribute and in that property names were changed as data models. In this post we will be looking into using AutoMapper's Queryable extension to retrieve the results based on selected columns. When  Mapper.Map<RoleViewModel>(data)  is called it retrieves all column values from table. The Queryable extension provides a way to retrieve only selected columns from table. In this particular case based on properties of  RoleViewModel . The previous approach that we implemented is perfect as far as this article ( 3 Tips for Using Telerik Data Access and AutoMapper ) is concern about performance where it states: While this functionality allows you avoid writing explicit projection in to your LINQ query it has the same fatal flaw as doing so - it prevents the query result from

Trim text in MVC Core through Model Binder

Trimming text can be done on client side codes, but I believe it is most suitable on MVC Model Binder since it would be at one place on infrastructure level which would be free from any manual intervention of developer. This would allow every post request to be processed and converted to a trimmed string. Let us start by creating Model binder using Microsoft.AspNetCore.Mvc.ModelBinding; using System; using System.Threading.Tasks; public class TrimmingModelBinder : IModelBinder { private readonly IModelBinder FallbackBinder; public TrimmingModelBinder(IModelBinder fallbackBinder) { FallbackBinder = fallbackBinder ?? throw new ArgumentNullException(nameof(fallbackBinder)); } public Task BindModelAsync(ModelBindingContext bindingContext) { if (bindingContext == null) { throw new ArgumentNullException(nameof(bindingContext)); } var valueProviderResult = bindingContext.ValueProvider.GetValue(bin

Data seed for the application with EF, MongoDB or any other ORM.

Most of ORMs has moved to Code first approach where everything is derived/initialized from codes rather than DB side. In this situation, it is better to set data through codes only. We would be looking through simple technique where we would be Seeding data through Codes. I would be using UnitOfWork and Repository pattern for implementing Data Seeding technique. This can be applied to any data source MongoDB, EF, or any other ORM or DB. Things we would be doing. - Creating a base class for easy usage. - Interface for Seed function for any future enhancements. - Individual seed classes. - Configuration to call all seeds. - AspNet core configuration to Seed data through Seed configuration. Creating a base class for easy usage public abstract class BaseSeed<TModel> where TModel : class { protected readonly IMyProjectUnitOfWork MyProjectUnitOfWork; public BaseSeed(IMyProjectUnitOfWork MyProjectUnitOfWork) { MyProject

Kendo MVC Grid DataSourceRequest with AutoMapper

Kendo Grid does not work directly with AutoMapper but could be managed by simple trick using mapping through ToDataSourceResult. The solution works fine until different filters are applied. The problems occurs because passed filters refer to view model properties where as database model properties are required after AutoMapper is implemented. So, the plan is to intercept DataSourceRequest  and modify names based on database model. To do that we are going to create implementation of  CustomModelBinderAttribute to catch calls and have our own implementation of DataSourceRequestAttribute from Kendo MVC. I will be using same source code from Kendo but will replace column names for different criteria for sort, filters, group etc. Let's first look into how that will be implemented. public ActionResult GetRoles([MyDataSourceRequest(GridId.RolesUserGrid)] DataSourceRequest request) { if (request == null) { throw new ArgumentNullExce

MongoDB navigation property or making it behave as ORM in .Net

This is an implementation to make models to have  navigation properties work like ORM does for us. What actually happens in ORM to make navigation properties work? Entity Framework has proxy classes implementation to allow lazy loading and eager loading implementation. While creating proxy classes it also changes definitions for actual classes to make navigation properties work to get values based on Model's for navigation properties. Most of ORMs work in same fashion like Telerik DataAccess has enhancer tool which changes class definition at compile time to enable navigation properties. In this implementation, we would retain the original class but we would have extension methods to allow initializing properties to have navigation proprieties work. Let's first create desire model on which we need to implement. I am picking up simple one-to-many relationship example from Person to Address. public class Person { public int PersonId { get; set; }

OpenId Authentication with AspNet Identity Core

This is a very simple trick to make AspNet Identity work with OpenId Authentication. More of all both approach is completely separate to each other, there is no any connecting point. I am using  Microsoft.AspNetCore.Authentication.OpenIdConnect  package to configure but it should work with any other. Configuring under Startup.cs with IAppBuilder app.UseCookieAuthentication(new CookieAuthenticationOptions { AuthenticationScheme = CookieAuthenticationDefaults.AuthenticationScheme, LoginPath = new PathString("/Account/Login"), CookieName = "MyProjectName", }) .UseIdentity() .UseOpenIdConnectAuthentication(new OpenIdConnectOptions { ClientId = "<AzureAdClientId>", Authority = String.Format("https://login.microsoftonline.com/{0}", "<AzureAdTenant>"), ResponseType = OpenIdConnectResponseType.IdToken, PostLogoutRedirectUri = "<my website url>",